Authentication and conventions

How API keys work, what each scope unlocks, how test mode is kept apart, and the rules every endpoint follows: amounts, dates, pagination, limits and errors.

Checked against the API code on October 13, 2026

Base URL

Text
https://api.ezuspay.app/v1

There is one URL for live and test. The key you send decides which environment you reach.

Call the API from your server. It does not send CORS headers to third-party domains, so a browser on your site cannot call it directly. That is also what keeps your key private.

API keys

Send the key in the x-api-key header on every request. The API does not read Authorization: Bearer.

Shell
curl https://api.ezuspay.app/v1/ping -H "x-api-key: ezpay_live_4f1c..."
PrefixEnvironmentFormat
ezpay_live_Live agencyprefix + 64 lowercase hex characters
ezpay_test_Test copy of the agencyprefix + 64 lowercase hex characters
  • Who creates keys. Agency admins, in Settings → Integrations → API keys. Other roles cannot see or create keys.
  • Shown once. Ezus Pay stores only a hash of the key. Copy it when it is created.
  • Expiry. Optional. The app offers no expiry, 90, 180 or 365 days.
  • Revocation. From the same screen. A revoked key stops working on the next request.
  • Check a key with GET /v1/ping. It returns the agency, the environment and the key's scopes.
ErrorWhen
401 API_KEY_MISSINGNo x-api-key header
401 API_KEY_INVALIDWrong format, unknown key, or a test key used on a live agency
401 API_KEY_REVOKEDThe key was revoked, or the agency was closed
401 API_KEY_EXPIREDThe expiry date has passed

Scopes

A key only reaches the endpoints its scopes allow. Give each integration the smallest set it needs.

ScopeUnlocks
bookings:writePOST /v1/bookings, POST /v1/bookings/:id/resync
payment-links:readGET /v1/checkout-fields, GET /v1/bookings/:id/payment-links, GET /v1/payment-links/:id
payment-links:writePOST /v1/bookings/:id/payment-links
transactions:readGET /v1/transactions, GET /v1/transactions/:id, GET /v1/bookings/:id/transactions, GET /v1/payment-links/:id/transactions
refunds:writePOST /v1/transactions/:id/refunds

GET /v1/ping needs no scope. A call without the right scope returns 403 SCOPE_INSUFFICIENT, with the missing scope in the message.

Test mode

Test mode is a full copy of your agency: its own bookings, links, payments, webhooks and a Stripe test account. Your settings, branding and checkout fields are shared with it.

  • Create a test key while the app is in test mode. It starts with ezpay_test_.
  • Every call with that key reads and writes test data only. GET /v1/ping returns "mode": "TEST".
  • Checkout links in test mode use your agency slug followed by --sandbox, and show a Test badge.
  • A test key can never reach live data.
  • An admin can reset test data from the app. Bookings, links and payments are erased. Keys, webhooks and the Stripe test account are kept.

Stripe test values that work on the checkout:

UseValue
Card that succeeds4242 4242 4242 4242, any future date, any CVC
Card that asks for 3D Secure4000 0027 6000 3184
SEPA Direct Debit (EUR)FR1420041010050500013M02606

Rate limits

Limits apply per API key, over a rolling minute.

RequestsLimit
Reads (GET)600 per minute
Writes (POST)60 per minute

Each response carries the standard RateLimit and RateLimit-Policy headers. Past the limit you get 429 RATE_LIMIT_EXCEEDED with a Retry-After header in seconds. Wait that long, then retry.

Responses and errors

Every response has the same envelope.

JSON
{ "success": true, "data": { } }
JSON
{
  "success": false,
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "Validation failed",
    "fields": [{ "path": "amountCents", "message": "Minimum amount is 1 unit" }]
  }
}
  • Branch on error.code. Messages are for humans and some are in French.
  • fields appears on validation errors only, with one entry per invalid field.
  • Unknown properties in a request body are ignored.
StatusMeaning
200 / 201Success. 201 when something was created
202Accepted, finished later. Used by refunds
401Key missing, invalid, revoked or expired
403Scope missing
404Resource not found in this agency
409Conflict with the current state
422Validation failed, or a business rule refused the request
429Rate limit reached
502Ezus or Stripe answered with an error

Endpoint-specific codes are listed in the API reference.

Conventions

  • Amounts are integers in the currency's smallest unit. Field names end in Cents. 144000 in EUR is 1,440.00 €. The maximum is 99999999.
  • Currencies use uppercase ISO 4217 codes. A booking defaults to the agency's currency. A payment link always uses its booking's currency.
  • Timestamps are ISO 8601 in UTC, with milliseconds: 2026-04-22T14:30:00.000Z.
  • Calendar dates in requests use YYYY-MM-DD.
  • IDs are UUIDs. The Ezus references ezusProjectId and ezusAlternativeId are free strings.
  • Pagination applies to transaction lists only: page (from 1) and limit (1 to 100, default 50). The response carries pagination.total and pagination.totalPages.
  • Idempotency. There is no Idempotency-Key header. POST /v1/bookings with an ezusProjectId returns the existing booking instead of a duplicate. Retrying a link or refund creation creates a second one, so check the first result before you retry.
  • Request bodies are JSON, up to 1 MB.