API Direct Integration Guide
Charge a saved card server-side, from your own backend, using its Cresora vault token.
The API Direct integration lets your server charge a card that is already saved with Cresora, without redirecting the payer anywhere. You reference the card by its Cresora vault token and call POST /transactions from your backend.
Raw card data is never accepted at this boundary. POST /transactions takes a Cresora vault token (cvt_…) — not a PAN, and not a gateway token (ADR-014, the Cresora-identity-only surface). A request carrying a card object is rejected with 400: unknown fields in a request body are an error, not silently dropped, so a misspelled field name fails loudly rather than changing what your call means.
A card becomes chargeable here by first being saved through a hosted-page save-card flow, which vaults it and returns the cvt_… reference. That is the only supported way to obtain one.
Because your server never receives or transmits primary account numbers on this path, the PCI scope of this call is not the scope of collecting a card. Your overall scope depends on how the card reaches Cresora in the first place — see the HPP guide and tokenization for that surface. Which SAQ applies to your business is your QSA's call.
Flow overview
1. The payer saves a card once, through a hosted-page save-card session
2. Your server receives the vault token (cvt_…) on the hpp_session.completed webhook
3. You store the vault token against your customer record
4. Your server calls POST /transactions with vault_token + use_type
5. Cresora processes the charge and returns the outcome
6. Cresora fires the transaction.captured webhook on approvalCharge a saved card
curl -X POST https://api.cresoracommerce.ai/api/v1/transactions \
-H "Authorization: Bearer csk_ab12cd34_xxxxxxxxxxxxxxxxxxxxxxxx" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: idem_$(uuidgen)" \
-d '{
"type": "SALE",
"merchant_id": "0190a1b2-c3d4-7e5f-8901-23456789abcd",
"amount": "50.00",
"vault_token": "cvt_01885fec-4444-7a31-9bbb-3e5b9a1f8c42",
"use_type": "ONE_TIME_FUTURE"
}'import requests, uuid
requests.post(
"https://api.cresoracommerce.ai/api/v1/transactions",
headers={
"Authorization": "Bearer csk_ab12cd34_xxxxxxxxxxxxxxxxxxxxxxxx",
"Idempotency-Key": f"idem_{uuid.uuid4()}",
},
json={
"type": "SALE",
"merchant_id": "0190a1b2-c3d4-7e5f-8901-23456789abcd",
"amount": "50.00",
"vault_token": "cvt_01885fec-4444-7a31-9bbb-3e5b9a1f8c42",
"use_type": "ONE_TIME_FUTURE",
},
)await fetch("https://api.cresoracommerce.ai/api/v1/transactions", {
method: "POST",
headers: {
Authorization: "Bearer csk_ab12cd34_xxxxxxxxxxxxxxxxxxxxxxxx",
"Content-Type": "application/json",
"Idempotency-Key": "idem_" + crypto.randomUUID(),
},
body: JSON.stringify({
type: "SALE",
merchant_id: "0190a1b2-c3d4-7e5f-8901-23456789abcd",
amount: "50.00",
vault_token: "cvt_01885fec-4444-7a31-9bbb-3e5b9a1f8c42",
use_type: "ONE_TIME_FUTURE",
}),
});use_type changes what the charge does
use_type declares your reuse intent and drives the card network's cardholder-initiated (CIT) vs merchant-initiated (MIT) classification. It is not a label — it changes the amount that is billed.
| Value | Initiated by | Behaviour |
|---|---|---|
ONE_TIME_FUTURE | the cardholder, present at checkout | Surcharge and convenience fee are computed server-side; the merchant's AVS policy applies |
RECURRING | the merchant | Billed as-is — no surcharge, no convenience fee, no AVS policy |
INSTALLMENT | the merchant | Billed as-is |
UNSCHEDULED_COF | the merchant | Billed as-is |
The intent must sit inside the scope the credential was saved with. Charging outside it returns 422 stored_credential_scope_violation.
422 stored_credential_not_chargeable means the vault entry predates the current charge-identity requirements. It cannot be repaired by retrying — the payer has to re-establish the card through a save-card hosted-page flow.
A decline is a 200
Approved and declined charges both return HTTP 200. A decline is a normal business outcome, not a transport error.
Discriminate on the response body: state = FAILED plus a decline_code. A handler that branches on the HTTP status alone will treat every decline as a success.
Authorize now, capture later
Not available on this endpoint. type: "AUTHORIZATION" returns 501, as does CARD_VERIFICATION — the gateway wire shapes for a vault-token authorize and a $0 verify are not vendor-confirmed yet, so neither carries a request variant.
Auth-only is available on the hosted-page rail: create the session with "capture_mode": "authorize" — lower case, and note that an unknown or misspelled field here is a 400, not a silent fall back to a sale. That materialises an authorization you then capture through the normal capture call.
curl -X POST https://api.cresoracommerce.ai/api/v1/transactions \
-H "Authorization: Bearer csk_ab12cd34_xxxxxxxxxxxxxxxxxxxxxxxx" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: idem_$(uuidgen)" \
-d '{
"type": "CAPTURE",
"merchant_id": "0190a1b2-c3d4-7e5f-8901-23456789abcd",
"parent_transaction_id": "0190d8a1-2b3c-7d4e-9f12-7a8b9c0d1e2f"
}'CAPTURE takes the parent's full authorized value — an amount in the body is ignored. Use PARTIAL_CAPTURE when you need less.
There is no Cresora capture deadline. Cresora does not expire authorizations on a timer and does not sweep them. The hold's lifetime belongs to the issuer, and you discover it lazily: a capture against a hold the issuer has already released comes back as a gateway decline, and the transaction is marked expired at that point. Plan around your issuer's window, not around a platform one.
Testing
Card values are entered on the hosted page, not sent to this endpoint, so the test-card list lives with the collection flow — see Testing & Sandbox.
Once you hold a sandbox vault token, the charge outcome is driven by the amount — the cents portion selects the scenario. Note the card mapping is its own set of values, not the R-code table used for ACH: see Testing & Sandbox for the card triggers, and the ACH guide for returns.