Statement Descriptors
A statement descriptor is the short line of text a buyer sees next to a charge on their card statement or banking app. A clear descriptor is the single best way to cut "I don't recognize this charge" disputes — buyers who recognize the merchant don't call their bank.
A descriptor has two parts, and they behave very differently:
| Part | What it is | Who controls it | Reliable? |
|---|---|---|---|
| Brand prefix | Your business name — the part a buyer reads as "who charged me". | Your merchant account, registered with your payment processor at boarding. | Set once, at the account level — not per charge. |
| Dynamic tail | A short per-transaction identifier appended after the brand (e.g. an order number). | You, per charge, through the Payments API. | Yes — this is the reliable lever. |
Put together, a statement typically reads like:
BRAND*ORD-1234
where BRAND is your registered business name and ORD-1234 is the dynamic
tail you sent on that specific charge.
The brand prefix comes from the descriptor registered on your merchant account. On many processors the bank shows that registered name and ignores any business name sent on an individual charge — so treat the brand as account-level configuration, not an API field.
To change the brand a buyer sees, update the registered descriptor on your merchant account with your payment processor — not through this API. What you can reliably set per charge is the dynamic tail. Build for the tail; don't expect a per-charge name to override your account descriptor.
How the descriptor is configured
Descriptor behavior is configured per merchant, on the Von Payments side today (there is no self-serve dashboard control yet). Ask your Von Payments contact to set it up or change it. A configuration has two pieces:
- A fixed identity — your business
name, plus optionalcity,country,phone,url, andpostal code. These describe your business and, where a processor accepts a structured descriptor, travel with the charge. - A dynamic-suffix rule — how the per-transaction tail is chosen. The rule has a source (where the tail comes from) and a fallback (what to do when that source is missing on a given charge).
Tail sources
The source decides which value becomes the tail. All except none read a
field from the charge's metadata:
| Source | Reads from | Typical use |
|---|---|---|
order_id | metadata.order_id | Show the order number on the statement. |
subscription_id | metadata.subscription_id | Show the subscription a renewal belongs to. |
customer_id | metadata.customer_id | Show your internal customer reference. |
custom | metadata.<your key> | Any metadata key you choose (e.g. product_name). |
none | — | No dynamic tail; the statement shows the brand only. |
Fallbacks
The fallback decides what happens when the source field is absent or empty on a charge:
| Fallback | Behavior when the source field is missing |
|---|---|
skip | No tail is added — the statement shows the brand alone. |
pi_ref | The last 8 characters of the payment intent ID are used as the tail, so the charge is still traceable. |
literal | A fixed text you configured is used as the tail. |
Feeding the tail from your code
You supply the tail's value per charge in the PaymentIntent metadata — the
same metadata object you already send on
POST /v1/payment_intents. The key is
fixed by your configured rule; you provide the value.
If your rule's source is order_id:
{
"amount": 4999,
"currency": "USD",
"metadata": { "order_id": "ORD-1234" }
}
If your rule uses a custom source with the key product_name:
{
"amount": 4999,
"currency": "USD",
"metadata": { "product_name": "Blue Widget" }
}
The buyer's statement then reads roughly BRAND*ORD-1234 or BRAND*Blue Widget
(subject to the length and character rules below). If you omit the configured key
on a charge, the rule's fallback applies.
A custom key that looks like buyer-identifying data — email, phone,
card, ssn, tax_id, address, and similar — is rejected when the rule is
set up. This is a deliberate guard: personal data must never end up printed on
a bank statement. Choose a non-PII key such as order_id or product_name.
Length and character rules
Card networks give the whole descriptor very little room, so a few rules apply before anything reaches the bank:
- Total length: 22 characters. The entire descriptor — brand prefix, the separator, and the tail — is capped at 22 characters. The prefix and separator consume part of that budget; the tail gets whatever remains.
- The tail is hard-truncated. If the tail is longer than the remaining room,
it is cut to fit — there is no
…ellipsis. A tail ofORDER-2026-000199can land on the statement asORDER-2026-00if that's all that fits. Keep tails short and put the most identifying characters first. - Printable ASCII only. Tail values are reduced to printable ASCII (letters,
digits, and common punctuation) before use; emoji, accented letters, and
non-Latin characters are stripped out. The configured brand name is likewise
limited to 22 printable-ASCII characters and can't contain
< > \ ' " *.
Because the tail is cut, not summarized, front-load the part a buyer or your
support team needs to recognize the charge. 1234-ORDER truncates more usefully
than ORDER-NUMBER-1234.
Confirming what was sent
Every POST /v1/payment_intents
response includes a resolved_descriptor object so your integration can confirm
exactly what was applied — no dashboard or database access needed.
- When a descriptor was applied,
resolved_descriptorechoes what was dispatched, including the resolved dynamicsuffix(the tail). Read it to see the exact tail that will reach the bank after truncation. - When nothing was applied, it's a skipped record naming the reason — for example, no descriptor is configured for your account. In that case the charge falls back to your processor's own default statement descriptor:
{
"resolved_descriptor": {
"kind": "skipped",
"reason": "no_config"
}
}
resolved_descriptor only ever reflects your own configured descriptor
fields and the tail you sent — it never contains buyer data, so it's safe to log
for reconciliation.
Related
- Payment Intent object — the
metadatafield and the full charge response shape - Payment Intents guide — creating a charge and attaching
metadata - Create a session — note that a session's
descriptionis not the statement descriptor - Error codes — provider length/character limits on descriptor and
metadatavalues