VCN guide
What a VCN is, the three types, the lifecycle and the window, idempotency, ceiling and error rules.
This guide gathers the rules that apply to all the VCN endpoints; each endpoint lists its own errors in the API Reference.
Overview
A VCN (Virtual Card Number) is a restricted-use virtual card, issued from your company's funding card, the corporate card it comes from. Each VCN has a usage window, a spending ceiling and a maximum number of authorizations (maxPurchaseQuantity). The PAN, CVV and expiration come back on issuance.
Types
Pick the type by what the card will pay for. The type defines the identifier you send and where the ceiling comes from:
| Type | To pay for | Identifier | Ceiling |
|---|---|---|---|
HOTEL | a lodging booking | hotelBooking.reservationId | derived from the booking cost, in amount |
AERIAL | an air booking | flightBooking.recordLocator | derived from the booking cost, in amount |
CUSTOM | a use you define, e.g. one card per supplier | reference | the limit.value you send |
Only CUSTOM can be updated: the HOTEL and AERIAL controls come from the booking.
Lifecycle
Issue
Create VCN returns the vcnId, the card data and the current controls. The VCN starts ACTIVE. Keep the vcnId with the identifier: it addresses the other routes, and no error returns it.
Get the card data again
Reveal VCN card data returns the PAN, CVV and expiration again, when you need them after issuance.
Top up and adjust
For CUSTOM, Update VCN tops up the ceiling, extends or shortens the window and adjusts the controls, without replacing the card.
Follow up
Get VCN returns the status, the usage window, the current ceiling and the controls, without the card data.
Expire or cancel
The VCN expires from the day after endDate. To close it earlier, for all three types, use Cancel VCN: cancellation is final, and the VCN does not go back to ACTIVE.
The sections below detail the rules of each step.
Platform identification (X-Partner-Id)
Every VCN call sends, in the X-Partner-Id header, the integrator platform (partner) it is made through. The header is required on every VCN route.
| Item | Rule |
|---|---|
| Who generates it | Conta Simples generates the ID and hands it to you at onboarding. You only send it. |
| Format | ptn_ followed by a 26-character ULID (Crockford base32, uppercase), e.g. ptn_01JQ8ZK7X4M8N2P5R6T7V8W9XY. It is not a name or a CNPJ. |
| Value | Immutable and case-sensitive: send it exactly as you received it. It is not a secret. |
| Where it goes | Always in the header. Never in the URL, as path or query, nor in the body. |
| Signature | The API does not require X-Partner-Id to be signed. If you want it protected, add "x-partner-id" to Signature-Input, and any change to the header will invalidate the signature. |
- Credential with more than one partner: a credential may be bound to more than one platform. On each call, send the
X-Partner-Idof the platform for that operation. The idempotency key is scoped per partner: the same key under another partner does not collide (see Idempotency and reissuance).
| Situation | Response |
|---|---|
| Header missing or malformed | 400 bad-request-exception, with errors[].field headers.x-partner-id |
| Partner not bound to your credential | 403 caller-not-authorized, with details.reason PARTNER_NOT_BOUND |
curl -X GET https://api-sandbox.contasimples.com/credit-cards/v1/vcns/{vcnId}/reveal \
-H "Authorization: Bearer {TOKEN}" \
-H "User-Agent: {your-app-name}/{version}" \
-H "X-Partner-Id: ptn_01JQ8ZK7X4M8N2P5R6T7V8W9XY"
Usage window
A VCN only authorizes purchases inside the startDate–endDate window. Both dates are calendar days in Brasília time — an exception to the API's general UTC date rule —, and "today" is the current day in Brasília.
- The window opens at 00:00 of
startDateand closes at the end of theendDateday:endDateis inclusive. - Without
startDate, the window starts on the issuance day, and the card is usable right away. createdAtandupdatedAtare UTC instants. For an issuance between 9 p.m. and midnight in Brasília,createdAtfalls on the day afterstartDate.- The window is not the physical card expiration, which comes in
card.expirationDate.
Default end per type
Without endDate in the request, the end is the type default. The default does not follow startDate: if the start falls on the default day or later, the window is inverted and issuance is rejected — in that case, send endDate.
| Type | Default end | Inverted window without endDate when |
|---|---|---|
HOTEL | checkOut + 20 days | checkOut was 20 or more days ago |
AERIAL | issuance day + 1 day | startDate is tomorrow or later |
CUSTOM | issuance day + 60 days | startDate is today + 60 days or later |
Date limits
| Rule | Create VCN | Update VCN |
|---|---|---|
| Start | startDate cannot be earlier than today; today is accepted at any time | startDate does not change |
| End after start | with both dates in the request, an endDate equal to or earlier than startDate → 400 bad-request-exception on body.endDate; without startDate, the earliest accepted endDate is tomorrow | the new endDate must come after the VCN's startDate |
| End and today | — | the new endDate cannot be earlier than today; today is accepted, and the card stays valid until the end of the day |
| 365-day cap | up to 365 days from the issuance day, the 365th accepted; sending startDate does not extend the cap | up to 365 days from issuance |
| Funding card expiration | the window end, sent or default, cannot go past the expiration of the funding card (the corporate card the VCN is issued from), which is not exposed | the same rule, when the request carries endDate |
Except where noted, the rejection is 400 invalid-vcn-request, without errors[], with the reason in message. For the funding-card cap, the reason is end date must not exceed the funding card expiration: shorten the window or contact Conta Simples.
Expiry and closing
- The VCN expires from the day after
endDate. On theendDateday itself it still accepts updates, and a new issuance on the same key still answers409. - Once expired, Update VCN answers
409 vcn-expired: do not top it up, issue another one. - There is no expired status: an expired VCN stays
ACTIVE. CompareendDatewith today's date in Brasília. - Shortening
endDatedoes not close the card. To close it, use Cancel VCN. - Top-ups do not push the validity, and changing the validity does not touch the ceiling: for both, send
limitandendDate. - The
startDateof an issued VCN does not change.
Idempotency and reissuance
Each type has an idempotency key, and it is also the identifier you send:
| Type | Key |
|---|---|
HOTEL | hotelBooking.reservationId — the booking id on the platform where it was made |
AERIAL | flightBooking.recordLocator — the locator (PNR, Passenger Name Record) |
CUSTOM | reference — a 1-to-255-character string you define; it decides what counts as "the same card" |
- The response returns the key in
reference, as a string, for all three types. In the request, onlyCUSTOMsendsreference. - The key is scoped to company,
typeand integrator platform (the call'sX-Partner-Id): the same key under anothertypeor another partner does not collide. - While the key's VCN is live (
ACTIVEorBLOCKED, inside its window), a new issuance answers409 vcn-already-issued, without the card data and without thevcnId. Store thevcnIdfrom the201together with the key. - For
CUSTOM, that409is the expected path: the card for thatreferencealready exists. Top it up with Update VCN; every newreferencecreates a new card. - The key is freed after expiry or cancellation. A new issuance with it creates another card, with a new PAN, and the previous
vcnIdstarts answering404on every route. - Do not put personal data in the key: it is stored and returned in the responses.
Ceiling and top-up
HOTELandAERIAL: the ceiling is derived from the cost,amount.rate + amount.taxes + amount.fees, which must be greater than zero. ForHOTEL, the extra-charges margin is added whenallowExtraChargesistrue: theextraChargesMarginvalue or, without it, the default margin — currently R$ 150.00 per night per guest, subject to change. To control the ceiling, sendextraChargesMargin.CUSTOM: the ceiling is thelimit.valueyou send, from0.01. OnlyCUSTOMis updatable;HOTELandAERIALcontrols come from the booking.- The ceiling is not a balance: the available amount is computed by the card network (
available = ceiling − spent), and the API does not return what was spent.
Top-up in the TOTAL period
⚠️ In the TOTAL period, the default, limit.value is the window's accumulated ceiling, not an increment. To add credit, send current ceiling + credit.
Example: ceiling 100.01 and a new R$ 80.00 sale → send 180.01. If you send 80.00, the ceiling drops below what was already spent, and every new authorization is declined.
The current ceiling comes back in the Create VCN 201, in the Update VCN 200 and in Get VCN: keep the latest one you receive.
Ceiling period
spendLimitPeriod | The ceiling covers |
|---|---|
TOTAL (default) | the whole window |
MONTHLY | each month; renews every month |
DAILY | each day; renews every day |
- The top-up arithmetic above only holds for
TOTAL. WithMONTHLYorDAILY,limit.valueis the ceiling for each month or day. - The period does not interact with
maxPurchaseQuantity, which counts authorizations. - On Update VCN, an omitted
spendLimitPeriodkeeps the current period, and changing the period does not reset what was spent: a card moving fromTOTALtoMONTHLYdoes not get a clean month.
Parallel top-ups
- Send
limit.expectedValuewith the ceiling you believe is current: the update only writes if it matches. - If it does not match — or if another update of the same VCN writes in between, which also happens without
expectedValueand on calls in quick succession —, the response is409 vcn-limit-conflict. Recompute the total from the latest ceiling and retry. Re-read the ceiling with Get VCN; the read may take a few moments to reflect the latest update.
Retrying calls
The API's general retry rules do not apply the same way to every VCN endpoint:
| Endpoint | After a 5xx or a timeout |
|---|---|
| Create VCN | Do not retry automatically. The card may have been issued, and the new attempt may get 409 vcn-already-issued without the vcnId. Contact support with the time of the call, the requestId from the body and the idempotency key. |
| Update VCN | 503 vcn-update-unavailable: retry. 500: contact support with the time and the requestId. |
| Reveal VCN card data | Retrying is safe. |
| Get VCN | Retrying is safe. |
| Cancel VCN | Retrying is safe: an already-cancelled VCN answers 200 with the same body. After a 500, a 503 or a timeout, retry the DELETE or check the status with Get VCN. |
On any endpoint, if the error persists, contact support with the time of the call and the requestId.
Reading right after writing
Reads are eventually consistent.
- Right after issuance, Update, Reveal, Get and Cancel may answer
404for a few moments: retry before concluding the VCN does not exist. - Right after an update or a cancellation, Get VCN may return the previous state for a few moments.
- Updates to the same VCN in very quick succession may get
409 vcn-limit-conflict(see Parallel top-ups).
Request body
Create VCN
- Send exactly one identifier, the one for your
type:hotelBooking,flightBookingorreference. One of another type is rejected with400 bad-request-exception, and the errorfieldpoints at it (e.g.body.referenceon aHOTEL). - Money has one field per meaning:
amountis the booking cost (required forHOTELandAERIAL, forbidden forCUSTOM);limitis the ceiling (required forCUSTOM, forbidden for booking types). - Amounts are decimals with up to 2 places. More places →
400, with no rounding. The currency is alwaysBRL. - Unknown keys are discarded silently — including
partner: the platform comes only from theX-Partner-Idheader and is never read from the body. nullis not the same as an absent field: on an optional field, it is rejected with400.referenceis a string. An object ({"id": …}) is rejected with400.requestoris required: a 1 to 100 character string, with a user's e-mail or an identifier from your system. An object ({"name": …}) is rejected with400.customFieldstakes up to 50 items, withnameup to 50 andvalueup to 100 characters: these are the limits the card network accepts. Above them,400.
Update VCN
- Send
Content-Type: application/json, therequestor(as in Create VCN) and only what you want to change: at least one oflimit,endDate,maxPurchaseQuantityandspendLimitPeriod. An empty body or one with only therequestor→400; a field you do not send does not change. - The body is closed. Any other key is rejected with
400 bad-request-exception, and the errorfieldpoints at the rejected key (e.g.body.expirationDate,body.limit.currency). That includes the immutabletype,reference,startDate,customFields, thelimitcurrency and the old nameexpirationDate.
Cancel VCN
- Send
Content-Type: application/jsonand a body with only therequestor, as in Create VCN. No body, norequestoror any other key →400 bad-request-exception.
Error format
Every error of the VCN endpoints has code, message and requestId; errors[] only comes with bad-request-exception.
- Handle the error by its
code. Themessagetext may change; the500always answersAn unexpected error occurred on the server. bad-request-exceptionis a format rejection, with oneerrors[]entry per field.invalid-vcn-requestis a window or ceiling rule rejection: it has noerrors[], and the reason is inmessage.errors[].fieldalways starts with the request part:body.,path.orheaders.(e.g.body.amount.rate,path.vcnId,headers.x-partner-id). When the rejection concerns the whole body — no field to change on Update VCN —, thefieldis justbody. Do not look for a bareamount.rate.requestIdidentifies the call: give it to support along with the time.- An invalid
vcnIdin the path — longer than 255 characters, containing/,\,?or#, or equal to.or..— gives400 bad-request-exceptionwithfieldpath.vcnId.
Evaluation order
When more than one rule fails, the error you get is the first one in this order:
- Create VCN:
409 vcn-already-issuedcomes before the date rules. A live key answers409even if the request dates are invalid. - Update VCN:
vcn-not-updatable→vcn-not-active→vcn-expired→vcn-limit-conflict(onexpectedValue) →400 invalid-vcn-request(endDaterules). - Reveal VCN card data: ownership comes before cancellation. A cancelled VCN of another company answers
404, not409.