VCNVCN guide

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:

TypeTo pay forIdentifierCeiling
HOTELa lodging bookinghotelBooking.reservationIdderived from the booking cost, in amount
AERIALan air bookingflightBooking.recordLocatorderived from the booking cost, in amount
CUSTOMa use you define, e.g. one card per supplierreferencethe 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.

ItemRule
Who generates itConta Simples generates the ID and hands it to you at onboarding. You only send it.
Formatptn_ followed by a 26-character ULID (Crockford base32, uppercase), e.g. ptn_01JQ8ZK7X4M8N2P5R6T7V8W9XY. It is not a name or a CNPJ.
ValueImmutable and case-sensitive: send it exactly as you received it. It is not a secret.
Where it goesAlways in the header. Never in the URL, as path or query, nor in the body.
SignatureThe 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-Id of 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).
SituationResponse
Header missing or malformed400 bad-request-exception, with errors[].field headers.x-partner-id
Partner not bound to your credential403 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 startDate and closes at the end of the endDate day: endDate is inclusive.
  • Without startDate, the window starts on the issuance day, and the card is usable right away.
  • createdAt and updatedAt are UTC instants. For an issuance between 9 p.m. and midnight in Brasília, createdAt falls on the day after startDate.
  • 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.

TypeDefault endInverted window without endDate when
HOTELcheckOut + 20 dayscheckOut was 20 or more days ago
AERIALissuance day + 1 daystartDate is tomorrow or later
CUSTOMissuance day + 60 daysstartDate is today + 60 days or later

Date limits

RuleCreate VCNUpdate VCN
StartstartDate cannot be earlier than today; today is accepted at any timestartDate does not change
End after startwith 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 tomorrowthe 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 capup to 365 days from the issuance day, the 365th accepted; sending startDate does not extend the capup to 365 days from issuance
Funding card expirationthe 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 exposedthe 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 the endDate day itself it still accepts updates, and a new issuance on the same key still answers 409.
  • 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. Compare endDate with today's date in Brasília.
  • Shortening endDate does 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 limit and endDate.
  • The startDate of an issued VCN does not change.

Idempotency and reissuance

Each type has an idempotency key, and it is also the identifier you send:

TypeKey
HOTELhotelBooking.reservationId — the booking id on the platform where it was made
AERIALflightBooking.recordLocator — the locator (PNR, Passenger Name Record)
CUSTOMreference — 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, only CUSTOM sends reference.
  • The key is scoped to company, type and integrator platform (the call's X-Partner-Id): the same key under another type or another partner does not collide.
  • While the key's VCN is live (ACTIVE or BLOCKED, inside its window), a new issuance answers 409 vcn-already-issued, without the card data and without the vcnId. Store the vcnId from the 201 together with the key.
  • For CUSTOM, that 409 is the expected path: the card for that reference already exists. Top it up with Update VCN; every new reference creates 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 vcnId starts answering 404 on every route.
  • Do not put personal data in the key: it is stored and returned in the responses.

Ceiling and top-up

  • HOTEL and AERIAL: the ceiling is derived from the cost, amount.rate + amount.taxes + amount.fees, which must be greater than zero. For HOTEL, the extra-charges margin is added when allowExtraCharges is true: the extraChargesMargin value or, without it, the default margin — currently R$ 150.00 per night per guest, subject to change. To control the ceiling, send extraChargesMargin.
  • CUSTOM: the ceiling is the limit.value you send, from 0.01. Only CUSTOM is updatable; HOTEL and AERIAL controls 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

spendLimitPeriodThe ceiling covers
TOTAL (default)the whole window
MONTHLYeach month; renews every month
DAILYeach day; renews every day
  • The top-up arithmetic above only holds for TOTAL. With MONTHLY or DAILY, limit.value is the ceiling for each month or day.
  • The period does not interact with maxPurchaseQuantity, which counts authorizations.
  • On Update VCN, an omitted spendLimitPeriod keeps the current period, and changing the period does not reset what was spent: a card moving from TOTAL to MONTHLY does not get a clean month.

Parallel top-ups

  • Send limit.expectedValue with 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 expectedValue and on calls in quick succession —, the response is 409 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:

EndpointAfter a 5xx or a timeout
Create VCNDo 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 VCN503 vcn-update-unavailable: retry. 500: contact support with the time and the requestId.
Reveal VCN card dataRetrying is safe.
Get VCNRetrying is safe.
Cancel VCNRetrying 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 404 for 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, flightBooking or reference. One of another type is rejected with 400 bad-request-exception, and the error field points at it (e.g. body.reference on a HOTEL).
  • Money has one field per meaning: amount is the booking cost (required for HOTEL and AERIAL, forbidden for CUSTOM); limit is the ceiling (required for CUSTOM, forbidden for booking types).
  • Amounts are decimals with up to 2 places. More places → 400, with no rounding. The currency is always BRL.
  • Unknown keys are discarded silently — including partner: the platform comes only from the X-Partner-Id header and is never read from the body.
  • null is not the same as an absent field: on an optional field, it is rejected with 400.
  • reference is a string. An object ({"id": …}) is rejected with 400.
  • requestor is required: a 1 to 100 character string, with a user's e-mail or an identifier from your system. An object ({"name": …}) is rejected with 400.
  • customFields takes up to 50 items, with name up to 50 and value up to 100 characters: these are the limits the card network accepts. Above them, 400.

Update VCN

  • Send Content-Type: application/json, the requestor (as in Create VCN) and only what you want to change: at least one of limit, endDate, maxPurchaseQuantity and spendLimitPeriod. An empty body or one with only the requestor → 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 error field points at the rejected key (e.g. body.expirationDate, body.limit.currency). That includes the immutable type, reference, startDate, customFields, the limit currency and the old name expirationDate.

Cancel VCN

  • Send Content-Type: application/json and a body with only the requestor, as in Create VCN. No body, no requestor or 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. The message text may change; the 500 always answers An unexpected error occurred on the server.
  • bad-request-exception is a format rejection, with one errors[] entry per field. invalid-vcn-request is a window or ceiling rule rejection: it has no errors[], and the reason is in message.
  • errors[].field always starts with the request part: body., path. or headers. (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 —, the field is just body. Do not look for a bare amount.rate.
  • requestId identifies the call: give it to support along with the time.
  • An invalid vcnId in the path — longer than 255 characters, containing /, \, ? or #, or equal to . or .. — gives 400 bad-request-exception with field path.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-issued comes before the date rules. A live key answers 409 even if the request dates are invalid.
  • Update VCN: vcn-not-updatable → vcn-not-active → vcn-expired → vcn-limit-conflict (on expectedValue) → 400 invalid-vcn-request (endDate rules).
  • Reveal VCN card data: ownership comes before cancellation. A cancelled VCN of another company answers 404, not 409.