Push your sales to one endpoint and we handle evidence, disputes, and the books — from the ChargeSecured register or from your own system.
The API is REST over HTTPS, JSON in and JSON out. Base URL:
https://chargesecured.com/api/v1
Most integrations need one call: post each completed sale. That single record gives us the transaction story we defend a chargeback with, and it populates the merchant's accounting, disputes, payouts and reporting.
Create a key in the merchant portal under Integrations. Keys are scoped and can be revoked instantly.
Send your key as a bearer token on every request.
# Authorization header (preferred) Authorization: Bearer cs_live_a1b2c3d4_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx # or, if your platform cannot set Authorization X-API-Key: cs_live_a1b2c3d4_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Keys carry scopes: sales:write, disputes:write, and read. A key is bound to exactly one merchant — it can never read or write another merchant's data. Use a cs_test_ key while you build.
We store only a hash of each key. If you lose it, revoke it and create another.
Send your own order id as external_id on every write. Retries, duplicate webhooks, and network timeouts are then safe: the same external_id always returns the original record with "idempotent": true instead of creating a second one.
Two modes. Send items and we price the sale using the merchant's configured tax, admin fee and surcharge. Or send totals when your POS already priced it, and we store your numbers verbatim so the books match your system exactly.
curl -X POST https://chargesecured.com/api/v1/sales \ -H "Authorization: Bearer $CS_KEY" \ -H "Content-Type: application/json" \ -d '{ "external_id": "order_88213", "source_name": "chargesecured-register", "occurred_at": "2026-09-01T18:22:00Z", "items": [ { "name": "Pendant lamp", "price": 189.00, "qty": 1 }, { "name": "Bulb, 4-pack", "price": 24.00, "qty": 2 } ] }'
{
"external_id": "order_88214",
"source_name": "in-store-terminal",
"totals": {
"subtotal": 237.00,
"discount": 10.00,
"tax": 18.73,
"admin_fee": 4.54,
"surcharge": 7.51,
"total": 257.78
}
}
| external_id | Your order id. Strongly recommended — it makes the call idempotent. |
| itemsor totals | Line items with price, qty, and optional taxable (defaults true). |
| totalsor items | Pre-computed amounts. total is required; processing_fee is derived if omitted. |
| occurred_at | ISO 8601. Defaults to now. Backdate when replaying history. |
| source_name | A short label for the system that sent this sale. Shown in the merchant's dashboard. |
| reference | Human-readable receipt number. Defaults to your external_id. |
Returns 201 with the stored sale, or 200 with "idempotent": true if that external_id was already recorded.
Use this to reconcile your POS against what we hold. Requires the read scope.
If your processor notifies you of chargebacks, forward them here. Posting an existing external_id with a new status updates the case rather than duplicating it.
{
"external_id": "cb_5521",
"sale_external_id": "order_88213",
"amount": 237.00,
"date": "2026-09-14",
"reason": "Product not received",
"status": "pending"
}
Passing sale_external_id links the dispute to the original sale, so the evidence package assembles automatically.
Returns the merchant the key belongs to and its scopes. Useful as a connection test during setup.
Add an endpoint under Integrations and we POST when a dispute is opened or changes status, so your POS can flag the order without polling.
POST https://your-pos.example.com/chargesecured
X-ChargeSecured-Event: dispute.created
X-ChargeSecured-Signature: sha256=<hmac>
{ "event": "dispute.created", "created_at": "...", "data": { ... } }
Verify the signature before trusting a delivery: compute an HMAC-SHA256 of the raw request body using your signing secret and compare it to the header.
| 401 | Missing, malformed, or revoked key. |
| 403 | Key lacks the required scope, or the merchant account is suspended. |
| 422 | Validation failed. The errors object names each field. |
| 404 | No record matches. |
| 500 | Something went wrong on our side. Retry with the same external_id — it is safe. |
To backfill history, post past sales with occurred_at set to the original date and totals taken from your own records. Because writes are idempotent on external_id, you can run the backfill repeatedly until it completes without creating duplicates.
Sales are tagged by origin, so a merchant can see at a glance what came from their POS, what came from the API, and what is sample data.