API basics

The Core Payments API is JSON over HTTPS at https://api.corepayments.app/v1. If you’ve used other payment APIs, most of this will feel familiar.

Authentication

Send your secret key as a bearer token:

curl https://api.corepayments.app/v1/charges \
  -H "Authorization: Bearer sk_test_..."
KeyStarts withUse it
Secretsk_test_, sk_live_On your server, for everything.
Restrictedrk_test_, rk_live_On a server or service that needs only some access.
Publishablepk_test_, pk_live_In the browser, with corepayments.js. It can only create payment methods.

A restricted key has no access, read or write for each resource: charges, refunds, customers, payment methods, balance transactions, events and webhook endpoints. Write includes read. GET requests need read; everything else needs write. A request the key can’t make gets 403 insufficient_scope. Give a reporting job read access to what it reports on, rather than a secret key that could also refund.

Create and roll keys in the dashboard under Developers → API keys. A key is shown once. Rolling a key can keep the old one working for up to 7 days, so you can deploy the new one without downtime. A request with a missing, revoked or expired key gets 401 invalid_api_key.

Test and live mode

The key decides the mode. Test keys work on test data only and never move money; live keys work on live data only. An ID from one mode is invisible to the other: retrieving a test charge with a live key is a 404. Every object has a livemode field that says which it belongs to.

Requests and responses

  • Send bodies as JSON with Content-Type: application/json. Responses are always JSON.
  • Amounts are integers in the currency’s smallest unit: cents for usd, so 2000 is $20.00. A decimal (20.00) or a string ("2000") is refused.
  • Timestamps such as created are Unix seconds.
  • IDs have a prefix that says what they are: ch_ for charges, cus_ for customers, pm_ for payment methods, re_ for refunds, evt_ for events and we_ for webhook endpoints.
  • Unknown fields are refused with 400 parameter_unknown, so a typo never passes silently. New optional fields may appear in responses at any time; ignore the ones you don’t use.

Idempotency

Network errors happen. To retry a POST safely, send an Idempotency-Key header:

curl https://api.corepayments.app/v1/charges \
  -H "Authorization: Bearer $COREPAYMENTS_SECRET_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: order_1042" \
  -d '{"amount": 2000, "currency": "usd", "payment_method": "pm_..."}'
  • A retry with the same key within 24 hours returns the first response, with the header Idempotent-Replayed: true, and does nothing else.
  • Reusing a key with different parameters is 409 idempotency_key_reused.
  • Retrying while the first request is still running is 409 idempotency_key_in_use. Wait a moment and retry.
  • Use something unique to the operation, such as your order ID, or a random UUID you store with it. Keys are per mode, and up to 255 characters.

Lists and pagination

List endpoints return a page, newest first:

{
  "object": "list",
  "data": [{ "id": "ch_3kTq9Xb2VnR8sLp1MwQ4zYcD", "object": "charge" }],
  "has_more": true,
  "url": "/v1/charges"
}
  • limit sets the page size, from 1 to 100. The default is 10.
  • When has_more is true, pass the last object’s ID as starting_after for the next page.
  • ending_before pages backwards. Use one or the other, not both.
  • Filters depend on the list. Charges take customer and status, refunds take charge, customers take email, and events take type.
curl "https://api.corepayments.app/v1/charges?limit=25&starting_after=ch_3kTq9Xb2VnR8sLp1MwQ4zYcD" \
  -H "Authorization: Bearer $COREPAYMENTS_SECRET_KEY"

Metadata

Charges, refunds, customers, payment methods and webhook endpoints take metadata: your own key-value pairs, such as an order or CRM ID. It’s returned on the object and in events, and it’s never shown to your customers.

  • Up to 50 keys.
  • Keys up to 40 characters.
  • Values are strings, up to 500 characters.
{ "metadata": { "order_id": "1042", "crm_id": "C-88" } }

Request IDs

Every response has a Request-Id header (req_…). Errors carry it as error.request_id too. The dashboard’s request log under Developers → Logs shows each request with its body and response. Quote the ID when you contact support.

Errors

Errors use the HTTP status and a JSON body:

{
  "error": {
    "type": "card_error",
    "code": "card_declined",
    "decline_code": "insufficient_funds",
    "message": "The card has insufficient funds to complete the purchase.",
    "param": null,
    "charge": "ch_3kTq9Xb2VnR8sLp1MwQ4zYcD",
    "request_id": "req_Q8aZ2nVx7LmT4pRk9sWc1bYe",
    "doc_url": "https://docs.corepayments.app/errors/card_declined"
  }
}

Branch on type and code, which are stable. message is written for people and may change. See errors for every code.

Versions

The API is versioned by date. Your account is pinned to the version current when it was created, 2026-10-01 for now. Changes that could break your integration ship as a new version that you upgrade to when you’re ready. Additions, such as new fields, new event types and new optional parameters, don’t need a new version.