Developers

Built to sit behind the POS you already run.

Push your sales to one endpoint and we handle evidence, disputes, and the books — from the ChargeSecured register or from your own system.

Overview

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.

Authentication

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.

Idempotency

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.

Record a sale

POST/api/v1/sales

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.

Let ChargeSecured price it

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 }
    ]
  }'

Send your own totals

{
  "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_idYour order id. Strongly recommended — it makes the call idempotent.
itemsor totalsLine items with price, qty, and optional taxable (defaults true).
totalsor itemsPre-computed amounts. total is required; processing_fee is derived if omitted.
occurred_atISO 8601. Defaults to now. Backdate when replaying history.
source_nameA short label for the system that sent this sale. Shown in the merchant's dashboard.
referenceHuman-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.

Read sales back

GET/api/v1/sales?limit=50
GET/api/v1/sales?external_id=order_88213

Use this to reconcile your POS against what we hold. Requires the read scope.

Record a dispute

POST/api/v1/disputes

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.

Verify a key

GET/api/v1/me

Returns the merchant the key belongs to and its scopes. Useful as a connection test during setup.

Webhooks

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.

Errors

401Missing, malformed, or revoked key.
403Key lacks the required scope, or the merchant account is suspended.
422Validation failed. The errors object names each field.
404No record matches.
500Something went wrong on our side. Retry with the same external_id — it is safe.

Migrating an existing POS

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.

Building an integration and need something the API doesn't cover yet? Tell us what you need.