More in Developers

API errors and limits

What every error from the Refficks API looks like, what each code means, how many requests you may make, and which failures to retry. For a developer calling POST /api/v1/events or the Server API.

The error envelope

Every error has one shape:

{
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "The request could not be processed.",
    "details": {
      "properties.amount": ["Amounts are integer minor units: 29900 for $299.00."]
    },
    "request_id": "01M51YGRBST524ZXY5YB283XR0"
  }
}
  • code is the one to switch on. It never changes for a given situation.
  • message is written for a person and can change. Log it; do not parse it.
  • details is present only when there is more to say: the failing fields on a 422, the missing ability on a 403, the existing event on an idempotency conflict.
  • request_id is the same value as the X-Request-Id response header.

Request ids

Every response, success or error, carries an X-Request-Id header. Refficks writes the same id in its logs for that request, including the work it queues afterwards, so support can follow one request from end to end.

If you send an X-Request-Id header yourself, Refficks uses yours and sends it back. Log the id beside every call you make. It is the first thing support asks for.

Error codes

HTTP error.code When Retry?
401 UNAUTHENTICATED No Authorization: Bearer header ("An API key is required."), a key Refficks does not know or that was revoked ("That API key is not valid."), or one that expired ("That API key has expired."). No. Fix the key.
402 ENTITLEMENT_EXCEEDED Your plan's limit for something is reached, such as live API keys or partners. details gives feature, limit and usage. No.
402 BILLING_READ_ONLY The workspace is read-only because its trial or subscription ended unpaid, for example "Your free trial has ended, so this workspace is read-only and its tracking is paused. Partner links still take people to your site, but no clicks or sales are recorded, and what was already approved can still be paid. After three months unpaid, the account closes. Subscribe in Settings › Billing to make changes and start tracking again." Server API changes are refused; reads still answer. No. Subscribe under Settings › Workspace › Billing.
402 TRACKING_PAUSED POST /events for something that happened while the workspace's tracking was paused, because it was unpaid: "Tracking was paused when this happened, because the workspace was unpaid, so it was not recorded." A sale made then is never recorded, even after subscribing. Refunds, disputes and credit notes are still recorded. No. Subscribing starts tracking again for what happens from then on.
403 FORBIDDEN A key that may not use this endpoint: a publishable key or an automation-platform key on POST /events ("That key is not permitted to perform this action."), a key without the ability a Server API endpoint needs (details.ability names it, for example partners:stage), or an automation-platform key asking for something kept for a person ("A key made for Zapier cannot archive a partner. That is for a person to decide, in Refficks or with a secret key."). No. Use the right key, or do it in the dashboard.
403 ACCOUNT_CLOSED "This account has been closed, so its API keys no longer work." Its secret keys stop; its publishable key keeps recording. No. A new key will not help.
404 NOT_FOUND No such address, no such record, or a record that belongs to another workspace. The last two look the same on purpose. No.
405 SERVER_ERROR (see below) The address exists but not for that method, such as a write to a read-only commission plan. No.
409 CONFLICT The request clashes with what is already true: a partner with that email exists, the partner is already in the program, an applicant is not yet approved, a link has nowhere to go, a code is already in use. message says which. No. Read the message and change the request.
409 IDEMPOTENCY_CONFLICT An Idempotency-Key reused with a different body on POST /events: "This idempotency key was already used with a different payload." details.event_id is the event already recorded under it. No. Use a new key for a new event.
422 VALIDATION_ERROR A field is missing, malformed, or not yours to set. details lists each field and why. No. Fix the fields.
429 RATE_LIMITED "Too many requests." See Rate limits. Yes, after waiting.
500 SERVER_ERROR Something broke on Refficks's side. Yes, with backoff.
502 INTEGRATION_ERROR A provider Refficks had to ask did not answer properly. Yes, later.
503 MAINTENANCE Refficks is in a maintenance window. message says why, details.until says until when if somebody said, and Retry-After is set when there is a time. Yes, after the window.

Switched off: Billing is off on Refficks until the owner turns it on, so BILLING_READ_ONLY and TRACKING_PAUSED cannot occur yet. When it is on, the Server API keeps answering reads and still lets an automation platform end its webhook subscriptions.

About 405. A request with the wrong method on a real address is answered with HTTP 405, but its error.code reads SERVER_ERROR and its message "An unexpected error occurred.", and it carries no Allow header. Go by the HTTP status in this one case: nothing broke, the method is not offered.

During maintenance the Server API is down, while POST /api/v1/events, the tracking links and the tracking script stay up, so sales are not lost.

Codes only the dashboard and portal see

A key never receives these; they are listed so a developer reading logs from the dashboard's own calls knows them.

HTTP error.code Meaning
401 SSO_REQUIRED "Your organization now signs in through its own provider." A session a password made has ended because the company requires single sign-on.
401 PORTAL_PREVIEW_ENDED A merchant's look at a partner's portal has ended.
403 NOT_A_PARTNER_HERE Signed in on a merchant's portal as somebody who is not that merchant's partner.
403 FORBIDDEN During a support session, making anything that outlasts it, for example "Refficks support cannot make an API key, because it would keep working after this session ends. Ask them to make it when they next sign in."

Rate limits

Surface Limit
POST /api/v1/events and POST /api/v1/referral-tokens 600 requests a minute, shared
/api/v1/server/* 300 requests a minute
Tracking: partner links (/r/{code}) and the tracking script's requests 1,200 requests a minute, per visitor's IP address
Helpdesk sidebar lookups 300 requests a minute
The Refficks WordPress plugin 300 requests a minute

The two server buckets are separate, so a large partner sync on the Server API cannot use up the allowance your shop needs to report orders.

How they are counted today. Refficks means the server, helpdesk and plugin limits to be counted per API key. On main they are counted per IP address the requests come from: two keys used from one server share one allowance, and one key used from two servers has two. This was confirmed for POST /events and the Server API by running them; the helpdesk and plugin limits are built the same way. If your integration uses several keys from one machine, budget for the limit once, not once per key.

A successful response carries X-RateLimit-Limit and X-RateLimit-Remaining. Slow down when X-RateLimit-Remaining gets low.

A 429 carries no Retry-After and no X-RateLimit-* headers. Wait for the minute to pass (up to 60 seconds) before retrying.

Other limits worth knowing:

Limit Value
Idempotency-Key 255 characters
per_page on any Server API list 100 (default 25)
Live webhook subscriptions per key 200
Events in one subscription request 50
Events a month No limit is enforced

Retrying safely

  1. Retry a 429, a 500, a 502, a 503, a timeout, or a dropped connection. Do not retry any other 4xx unchanged: the same request gets the same answer.
  2. Back off. Wait a second, then double the wait after each failure, and add a little randomness so many clients do not retry at once. For a 429, wait until the minute has passed. For a 503 with Retry-After, wait that many seconds.
  3. For POST /api/v1/events, reuse the same Idempotency-Key and the same body. A retry of an event that did arrive is answered 200 with "status": "duplicate", and nothing is counted twice. Changing the body under the same key is a 409 IDEMPOTENCY_CONFLICT.
  4. For Server API writes, look before you retry. They take no idempotency key. Some repeats are refused (a second partner with the same email, a second enrolment), but a second POST /server/tracking-links makes a second link. After a timeout, read first, for example GET /server/partners/lookup?email=… or GET /server/tracking-links?partner_id=…, and only create what is missing.

When to contact support

Write to support@refficks.com when a 500 keeps happening for the same request, or when an answer contradicts this page. Include:

  • the X-Request-Id (or error.request_id) of a failing request;
  • the method and path, such as POST /api/v1/events, and the time in UTC;
  • the HTTP status and the error.code and error.message;
  • the request body, with customer emails removed.

Never send a secret key, a webhook signing secret, or a partner's payout details. Support does not need them and will not ask for them. The first 12 characters of a key, as shown on Settings › Developer › API keys, are enough to say which key it was.