Capturia measures its ads and site traffic with the Meta pixel and Google Analytics. You can decline at any time. Learn more

FR

Purchases

2 endpoints

post/v1/purchases

Record a purchase made outside Capturia

A store, a course platform or a backend sends a purchase made on its side. Capturia resolves the buyer the same way as an event (external_id → phone → email, contact created if needed), records the purchase on the contact (customer of this offer since the purchase date), files the purchase in its offer's deal and moves the card of the current deal to the won stage. The purchase also starts the "Payment received" automations, the "Purchase completed" gateway of your systems and the "Custom event" automations listening to the purchase event, recorded on the contact.

Idempotency

A purchase is unique by its external_id: sent again with the same offer, amount and currency, the response returns the purchase already recorded (200, created: false) without restarting anything; sent with another offer, amount or currency, it is rejected (409 purchase_conflict). The Idempotency-Key header remains available for retries.

Availability

Online sales must be open for the account, otherwise 403 feature_not_available. An archived contact rejects the purchase (422 contact_archived): restore it, then send the purchase again.

Past purchases

A purchase dated (purchased_at) more than two days ago, for example when importing a sales history, is recorded on the contact and in its offer's deal, and the purchase event is recorded; but it is not a sale of the day: neither the "Payment received" automations, nor the "Purchase completed" gateway, nor the "New sale" bell start for it.

Required scopes: events:ingest

Headers

NameTypeRequiredDescription
Idempotency-KeystringoptionalIdempotency key (UUID v4 or ULID recommended). A second request with the same key and the same payload returns the original response without repeating the operation. Stored for 24h. Optional but recommended.

Request body

PropertyTypeRequiredDescription
external_idstringrequiredIdentifier of the purchase in the source system (order number, transaction ID). Makes the call idempotent: the same external_id sent again unchanged returns the purchase already recorded.
offerobjectrequiredThe purchased offer: key (required), its stable identifier in the source system (SKU, product identifier); name (optional), the name shown on the contact, read on the first purchase with this key.
amount_centsintegerrequiredAmount paid, in cents, before taxes.
currencystringoptionalISO 4217 currency of the amount.
purchased_atstringoptionalPurchase date, today at the latest. Default: when the request is received.
contactEventIngestContactrequiredIdentity of the contact the event targets. At least one identifier is required among external_id, email, phone. The server resolves the contact in the order external_id+external_source → phone → email; the identity fields you send enrich the contact non-destructively (a value that is already set is never overwritten).
source_labelstringoptionalFree-form label identifying the source (shown on the contact).

Responses

StatusDescription
200

Purchase already received under this external_id, returned as is.

201

Purchase recorded.

400

The request is malformed (missing header, invalid JSON, invalid parameter).

401

Unknown API key or invalid format.

403

Authorization denied. Two causes, distinguished by code: insufficient_scope (the key lacks the required scope — details.required_scope) or feature_not_available (the public API or the targeted module isn't included in the account's plan — details.feature names the feature, e.g. public_api, quotes). A plan denial is final for the key: don't retry; the account must change plans. A feature not yet launched for the account (online_sales) answers the same code, with a message that says so. An account whose access the Capturia team suspended or revoked answers account_suspended to every request while the suspension lasts.

404

The requested resource doesn't exist (or isn't within the tenant's scope).

409

A resource with a conflicting unique identifier already exists.

422

Parameter or body validation failed.

429

The per-window request quota has been exceeded for this API key or endpoint.

500

Unexpected server error. Always include the request_id when contacting support.

Example request

curl -X POST 'https://api.capturia.io/v1/purchases' \
  -H 'Authorization: Bearer cap_live_<your-key>' \
  -H 'Content-Type: application/json' \
  -d '{
  "external_id": "commande-10482",
  "offer": {
    "key": "formation-vente-avancee",
    "name": "Formation vente avancée"
  },
  "amount_cents": 49700,
  "currency": "CAD",
  "purchased_at": "2026-10-18T14:32:00-04:00",
  "contact": {
    "email": "[email protected]",
    "first_name": "Alex"
  },
  "source_label": "Boutique en ligne"
}'
post/v1/purchases/refunds

Record a purchase refund

Sets the total amount refunded for a purchase received through POST /v1/purchases (the running total, not the latest refund). Once fully refunded, the purchase becomes refunded and the contact is no longer a customer of this offer; a lower total, after a cancelled refund, makes the contact a customer again. Sending the same total again changes nothing. A $0 purchase cannot be refunded. A total greater than the purchase amount is rejected (422 refund_exceeds_amount) and nothing changes. An unknown purchase returns 404 purchase_not_found.

Required scopes: events:ingest

Headers

NameTypeRequiredDescription
Idempotency-KeystringoptionalIdempotency key (UUID v4 or ULID recommended). A second request with the same key and the same payload returns the original response without repeating the operation. Stored for 24h. Optional but recommended.

Request body

PropertyTypeRequiredDescription
external_idstringrequiredThe external_id of the refunded purchase.
refunded_centsintegerrequiredTotal amount refunded for this purchase, in cents (the running total, not the latest refund). At most the purchase amount: above it, the refund is rejected (422 refund_exceeds_amount).

Responses

StatusDescription
200

Refund recorded.

400

The request is malformed (missing header, invalid JSON, invalid parameter).

401

Unknown API key or invalid format.

403

Authorization denied. Two causes, distinguished by code: insufficient_scope (the key lacks the required scope — details.required_scope) or feature_not_available (the public API or the targeted module isn't included in the account's plan — details.feature names the feature, e.g. public_api, quotes). A plan denial is final for the key: don't retry; the account must change plans. A feature not yet launched for the account (online_sales) answers the same code, with a message that says so. An account whose access the Capturia team suspended or revoked answers account_suspended to every request while the suspension lasts.

404

The requested resource doesn't exist (or isn't within the tenant's scope).

409

A previous request with the same Idempotency-Key used a different payload, or is still being processed.

422

Parameter or body validation failed.

429

The per-window request quota has been exceeded for this API key or endpoint.

500

Unexpected server error. Always include the request_id when contacting support.

Example request

curl -X POST 'https://api.capturia.io/v1/purchases/refunds' \
  -H 'Authorization: Bearer cap_live_<your-key>' \
  -H 'Content-Type: application/json' \
  -d '{
  "external_id": "commande-10482",
  "refunded_cents": 49700
}'