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. It 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.
To change the brand a buyer sees, update the registered descriptor on your merchant account with your payment processor — many banks show that registered name and ignore any business name sent on an individual charge. The tail is what you set per charge.
How the descriptor is configured
Descriptor behaviour is configured per merchant by Von Payments — 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).
Where the brand line actually comes from
⚠️ Your configured name is not always what prints as the brand. It depends on the processor connection your account uses, and the difference matters if you are building a preview of what the buyer will see.
| Connection style | What supplies the brand | What your name does |
|---|---|---|
| A structured-descriptor connection | Your configured name is sent with the charge as the brand. | It is the brand. |
| A prefix-and-suffix connection | The prefix comes from your own account settings held at the processor, and those take precedence. | It does not print. It only estimates the character budget left for the tail — and that estimate is wrong when the two differ. |
On a prefix-and-suffix connection the printed result is PREFIX* tail, and the separator (* plus a space) counts against the total — so two characters of your budget go to punctuation before any of your text does.
Do not build a buyer-facing preview screen from your configuration alone. Use it to see what you have configured and to compute the tail your metadata will produce; confirm the final printed descriptor at the processor. Confirming what was sent covers how.
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. 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 summarised, front-load the part that identifies 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. Read it to see the exact tail that will reach the bank after truncation.kindtells you which of two applied shapes you got, and it never names a payment provider. Branch on these three values only:kindShape Meaning suffix{ kind, suffix }A dynamic tail appended to the descriptor prefix registered on your processing account. descriptor{ kind, descriptor: { name, description, city, country, phoneNumber, url, postalCode } }A full descriptor block sent with the charge. skipped{ kind, reason }Nothing was applied. See the reasons below. -
When nothing was applied, it's a skipped record naming the reason. The charge then falls back to your processor's own default statement descriptor:
{
"resolved_descriptor": {
"kind": "skipped",
"reason": "no_config"
}
}
Why a descriptor was skipped
Four reasons reach you:
reason | What happened | What to do |
|---|---|---|
no_config | No descriptor was sent for this charge. | If your account has no descriptor configured, ask your Von Payments contact to set one up. Otherwise see the caution below: this reason also covers a transient lookup failure and a configuration that produced nothing for this charge. |
kill_switch | Descriptor passthrough is switched off platform-wide. | Nothing on your side. Charges still succeed; they carry your processor's default descriptor until it is switched back on. |
binder_unsupported | The processor connection your account uses does not accept a descriptor from us today. | Nothing on your side — this is a property of the connection, not of your configuration. |
invalid_for_provider | Your configured descriptor breaks a length or format limit the processor enforces (for example a name shorter than 5 characters). The charge went through without it, so your account's default descriptor applies. | Ask your Von Payments contact to correct the descriptor; it is applied again from the next charge after that. |
On a structured-descriptor connection, an optional field (description, city, country, phoneNumber, url, postalCode) that breaks a processor limit is dropped on its own and the rest of the descriptor, including your name, still goes out. The usual case is a per-charge description that resolves to a short order number. That is not a skip: resolved_descriptor reads kind: "descriptor" and simply lacks the field. Compare it with your configuration to spot one.
no_config does not always mean "you have not configured one." It means no descriptor was sent, for one of three reasons that report identically:
- the account has no descriptor configured;
- the configuration read failed (it never fails the charge, which proceeds without a descriptor);
- on a connection that appends a dynamic suffix, the configuration was read but resolved to an empty suffix for this charge, so the connection's default applies.
On an account with a descriptor configured, no_config is not a sign that the configuration was lost; do not re-create it on the strength of this value.
Descriptor passthrough is resolved only on a real processor connection, so verify a descriptor with a live key, or on a sandbox account with its own processor test account. A merchant account's test key cannot take a test payment (sandbox_account_required).
The SDK types and the contract also list a fourth value, circuit_open. It is declared and not emitted — a configuration read failure reports no_config — so do not write a branch that waits for it.
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