Matchai Docs

ErrorsComing soon

The Matchai API error format, every error code with its HTTP status and what to do about it, which requests are safe to retry, and how to use request ids.

Coming soon. This is documented ahead of launch so you can plan your integration; it is not available yet.

Every error from the Matchai API has the same JSON shape. Branch on the stable code, and show the message to the person using your app: it says what happened and what to do next.

The error format

Response (400)
{
  "error": {
    "type": "invalid_request_error",
    "code": "parameter_invalid",
    "message": "price.amount must be greater than 0.",
    "param": "price.amount",
    "doc_url": "https://matchai.co/docs/developers/errors#parameter_invalid",
    "request_id": "req_7Hq2LmZ9xV4c"
  }
}

Field

Type

Three habits make errors painless:

  • Branch on code, not on message. Codes are stable. Messages are written for people, follow the account's language, and can be reworded.
  • Treat unknown values as unknown, not as a crash. New codes and types can be added without a new version. If you meet a code you do not know, fall back to its type and its HTTP status. An unknown type is handled like api_error.
  • Log the request id. See request ids.

Status codes

StatusTypeMeaningRetry?
202NoneNot an error. A final action is waiting for the account holder's approval. Nothing has happened yet.No. Wait for approval.resolved.
400invalid_request_errorThe request is malformed or a parameter is invalid.No. Fix the request.
401authentication_errorThe key or token is missing, expired, revoked or wrong.After refreshing an expired token, once.
402energy_errorNot enough Matchai Energy, or the connection reached its Energy limit.No, not until there is Energy again.
403permission_errorThe credential is valid, but cannot do this.No.
404not_found_errorIt does not exist, or this account cannot see it.No.
409conflict_errorIt clashes with the current state.No. Change the request.
422unprocessable_errorThe request is well formed but breaks a product rule.No. Change the request.
429rate_limit_errorToo many requests for this connection.Yes, after Retry-After seconds.
500api_errorSomething went wrong on Matchai's side.Yes, with the same Idempotency-Key.

Request ids

Every response, successful or not, carries a Matchai-Request-Id header. Errors repeat it as error.request_id. Log it with every failed call. When you contact Matchai support, include it: it lets Matchai find exactly what happened on that request.

Response header
Matchai-Request-Id: req_7Hq2LmZ9xV4c

Retry safely

Retrying is safe when the same request can be repeated without doing the action twice. That is what the Idempotency-Key header is for.

  • Send an Idempotency-Key on every POST. A UUID works.
  • A retry with the same key within 24 hours returns the first response instead of acting twice. A retry of a final action under Ask me returns the same pending approval.
  • Reusing a key with a different body returns 409 idempotency_key_reused. Make a new key for a new request.

Retry network errors and timeouts, 429 and 500. Do not retry 400, 403, 404, 409 or 422 unchanged: they fail the same way again. A 402 keeps failing until the account has Energy again or the account holder raises the connection's limit.

Wait before each retry: honour Retry-After when there is one, and otherwise wait longer each time, with a little randomness so that many clients do not retry together.

retry.mjs
import { randomUUID } from 'node:crypto';

const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));

// 1 s, 2 s, 4 s... plus up to a second of jitter.
const backoff = (attempt) => 2 ** (attempt - 1) * 1000 + Math.random() * 1000;

export async function postWithRetry(url, body, { attempts = 4 } = {}) {
  const idempotencyKey = randomUUID(); // The same key on every attempt.

  for (let attempt = 1; ; attempt += 1) {
    let response;
    try {
      response = await fetch(url, {
        method: 'POST',
        headers: {
          Authorization: `Bearer ${process.env.MATCHAI_API_KEY}`,
          'Content-Type': 'application/json',
          'Idempotency-Key': idempotencyKey,
        },
        body: JSON.stringify(body),
      });
    } catch (networkError) {
      if (attempt >= attempts) throw networkError;
      await sleep(backoff(attempt));
      continue;
    }

    const retryable = response.status === 429 || response.status >= 500;
    if (!retryable || attempt >= attempts) return response;

    const retryAfter = Number(response.headers.get('Retry-After'));
    await sleep(retryAfter > 0 ? retryAfter * 1000 : backoff(attempt));
  }
}

Error codes

Each code below is the target of the doc_url that comes with it.

Invalid requests

parameter_invalid

400 · invalid_request_error. A field is missing, has the wrong type, or is out of range. param names the field, with dots for nested fields, such as price.amount. Fix the request: sending it again unchanged fails again.

Other 400 answers, such as a body that is not valid JSON, carry the same type. Treat any 400 as "this request is wrong".

Authentication

token_expired

401 · authentication_error. The access token has expired. With Connect with Matchai, exchange your refresh token for a new access token, then repeat the request once. If the refresh fails, the connection has ended and the professional needs to connect again.

A 401 can also mean that the key or token is missing, mistyped or revoked, or that the connection was ended. Those carry the same type. Ask for a new API key, or send the professional through the connect flow again.

Energy

Matchai Energy is never a number in the API. These errors say only that there is not enough.

insufficient_energy

402 · energy_error. The account does not have enough Matchai Energy for this action. Nothing was done and nothing was spent. The account holder can get more from the Matchai Store, and GET /v1/me reports the state of their Energy (full, comfortable, low or empty). Show the message to the person, and try again once they have topped up.

energy_limit_reached

402 · energy_error. This connection has used the Energy limit the account holder set for it. Nothing was done and nothing was spent. GET /v1/me/connection reports energy_limit_reached: true. The account holder decides whether to raise the limit; your app should not retry in a loop.

Permissions

scope_missing

403 · permission_error. The key or token is valid, but lacks the scope this action needs. The message names it. Create a new API key that includes the scope, or send the professional through the connect flow again with a longer scope list.

not_allowed

403 · permission_error. The account's role does not allow this. The connection acts with the account holder's own permissions and no more, so something they cannot do in Matchai, such as editing a team's work as a member without edit rights, they cannot do through your app either. Tell the person, and do not retry.

Not found

resource_not_found

404 · not_found_error. The thing does not exist, or this account cannot see it. Matchai gives the same answer for both, so a response never confirms that something exists in someone else's account. Check the id, and check that you are using the credential of the account you meant.

Conflicts

contact_exists

409 · conflict_error. The email or phone number you sent already belongs to one of the account's contacts, so nothing was added. The id of the existing contact is in error.param. Update that contact instead, or use POST /v1/enquiries, which matches existing contacts for you.

subdomain_taken

409 · conflict_error. The subdomain you asked for is not available. Subdomains use lowercase letters, digits and hyphens. Choose another and try again.

calendar_conflict

409 · conflict_error. The time overlaps another appointment, and the response lists the appointments that clash. Offer another time. Only send book_over_conflict: true once the account holder has agreed to the clash.

slot_unavailable

409 · conflict_error. The time you asked for was taken in the meantime, for example between showing a visitor the free times and recording their visit request. Fetch the free times again and offer another.

idempotency_key_reused

409 · conflict_error. You sent an Idempotency-Key that Matchai already saw within the last 24 hours, with a different request body. Use a new key for a new request. Reuse a key only to repeat the exact same request.

Refused by a product rule

rooms_exceed_count

422 · unprocessable_error. A listing's bedroom or bathroom count cannot go below the rooms listed on it. Remove the room first, then lower the count. param names the field, such as property.bedrooms.

recipient_suppressed

422 · unprocessable_error. Matchai will not send email to this address, because earlier messages to it bounced or were reported as unwanted. Tell the person, and reach the contact another way.

contact_blocked

422 · unprocessable_error. This contact is blocked, so Matchai refuses to message them. Tell the person, and do not retry.

content_not_allowed

422 · unprocessable_error. The content you sent breaks Matchai's content rules. The message says what to change. Do not retry it unchanged.

handle_not_routable

422 · unprocessable_error. Publishing the Matchai Page failed because the account's username cannot be a web address yet. The account holder needs to choose a different username in Matchai.

Rate limits

rate_limited

429 · rate_limit_error. Too many requests for this connection. The response carries Retry-After, the number of seconds to wait. See rate limits.

Matchai errors

internal_error

500 · api_error. Something went wrong on Matchai's side. It is safe to retry with the same Idempotency-Key, a few times, with growing delays. If it keeps failing, contact Matchai support with the request id.

On this page