Custom Fields
Custom fields let you collect structured data from customers during the checkout process. You can define up to 3 fields per checkout session, supporting text inputs, numeric inputs, and dropdowns. A practical use case is if you run a Discord server and need to grant roles after purchase. You can add a custom field fordiscord_username during checkout. When the customer pays, the PAYMENT_COMPLETED webhook event is triggered. You can then use the submitted discord_username value to automate the role assignment.
Other examples include collecting server preferences, referral codes, or any data your fulfillment logic needs.
Metadata
Metadata lets you attach arbitrary key-value string pairs to a checkout session. Unlike custom fields (which are filled by the customer), metadata is set by your application when creating the session and is immutable after creation. Metadata flows through the entire pipeline: checkout session → order → webhook payloads → fulfillment webhooks. Constraints:- Maximum 20 keys per session
- Key: max 40 characters
- Value: max 500 characters
- Keys and values must be strings
Grab the secrets
In your store, create a new webhook. Copy and securely store the webhook secret.You’ll also need your Store ID.
.env
Prepare backend
Set up an Express server with TypeScript:Create a
src folder and add an index.ts file:src/index.ts
Create a checkout session with custom fields and metadata
When creating a checkout session via the V2 API, pass
The response includes a
display.fields for custom fields and metadata for developer key-value pairs.There are three field types:| Type | Description | Config options |
|---|---|---|
text | Free-form text input | default_value, minimum_length, maximum_length |
numeric | Digits-only input | default_value, minimum_length, maximum_length |
dropdown | Select from a list | default_value, options (array of label/value pairs) |
session_id. Use it to build the checkout URL or redirect the customer.Handle PAYMENT_COMPLETED with custom fields and metadata
When a payment succeeds, the
PAYMENT_COMPLETED webhook fires. The data.order object contains customFields (customer-submitted values) and metadata (your developer-defined key-value pairs).Custom Field Reference
| Property | Type | Required | Description |
|---|---|---|---|
key | string | Yes | Unique identifier, alphanumeric and underscores, max 200 chars |
label.type | string | Yes | Must be "custom" |
label.custom | string | Yes | Display label shown to customer, max 50 chars |
type | string | Yes | "text", "numeric", or "dropdown" |
optional | boolean | No | Default: false |
text.default_value | string | No | Default value for text fields |
text.minimum_length | integer | No | Minimum character length (min: 0) |
text.maximum_length | integer | No | Maximum character length (max: 255) |
numeric.default_value | string | No | Default value for numeric fields |
numeric.minimum_length | integer | No | Minimum digit length (min: 0) |
numeric.maximum_length | integer | No | Maximum digit length (max: 255) |
dropdown.default_value | string | No | Pre-selected option value |
dropdown.options | array | Yes | 1-200 items, each with { label, value } |
Metadata vs Custom Fields
| Custom Fields | Metadata | |
|---|---|---|
| Set by | Customer (at checkout) | Developer (at session creation) |
| Purpose | Collect user input (Discord name, etc.) | Internal tracking (user IDs, campaigns) |
| Max count | 3 fields | 20 keys |
| Mutable | Submitted at payment confirmation | Immutable after session creation |
| In webhooks | data.order.customFields | data.order.metadata |
| Types | text, numeric, dropdown | string key-value pairs only |
Validation Rules
The server strictly validates submitted custom field values:- Required fields must have a non-empty value
- Text: enforces
minimum_lengthandmaximum_lengthconstraints - Numeric: value must contain only digits, enforces length constraints
- Dropdown: value must match one of the defined
options[].valueentries - Duplicate keys are rejected
- Unknown keys not defined in the session are rejected
- Submitting
custom_fieldswhen the session has none defined returns an error - Maximum of 3 custom fields per session
Webhook Headers
Every webhook delivery includes these headers:| Header | Description |
|---|---|
X-Pandabase-Signature | HMAC-SHA256 signature of the JSON body using your webhook secret |
X-Pandabase-Timestamp | Unix timestamp of the delivery |
X-Pandabase-Idempotency | Unique delivery ID for deduplication |
