Using the API
Errors
When a request fails, the status says what kind of failure and the body says which.
The error body#
Every refusal has the same shape, whatever the endpoint:
{
"error": {
"code": "VALIDATION_FAILED", // always — what to branch on
"message": "Please check …", // always — English, for a person
"key": "err_please_check_…", // sometimes — the sentence's id
"params": { … }, // sometimes — what fills it
"details": { "customer.phone": "…" }, // sometimes — per field, or more about it
"detailKeys": { … } // sometimes — each detail's sentence id
}
} - Branch on
code, together with the HTTP status. Codes are part of v1 and do not change. - Show
messageto a person if you like; do not parse it. It is written for whoever is reading your logs and may be reworded at any time. -
keyandparamsare the sentence's id and its values, the same ones the staff app uses to say it in eight languages. Use them if your own app translates. -
detailssays more. For a422it names each field that is wrong by its dotted path —customer.phone,serviceIds.0— with one sentence each.
Error codes#
| Status and code | What it means | How to fix it |
|---|---|---|
400 BAD_REQUEST | A write (POST, PUT or PATCH) was sent with a query string. Every field of a write goes in its JSON body; only GET takes query parameters. | Move the fields from the URL into the body, and send the path with nothing after it. |
401 UNAUTHORIZED | No key was sent, or the key is not one we know: mistyped, revoked, expired, or made by an owner who has since been removed. | Check the Authorization header is Bearer and the whole key. If the key was revoked or expired, make a new one. |
403 FORBIDDEN | The key is good but may not do this: it lacks the scope the request needs, it is limited to some lines and tried to make a new one, or the business is the demo. | Read the message — it names the missing scope. Ask the owner to edit the key's permissions in Settings → Integrations. |
403 PLAN_LIMIT | The business's plan does not include API access. Keys and endpoints are kept, but every call is refused until the plan includes them again. | API access and webhooks come with Basic and every plan above it. The owner can change plan in Settings → Billing. |
404 NOT_FOUND | There is no such line, desk or visit — or there is, but not one this key may see. The two are deliberately the same answer. | Check the id. If it is right, the key is limited to other lines or desks; the owner can widen it. |
409 CONFLICT | The request is fine but the business is not in a state to take it: a desk asked to end its shift while it is serving somebody, a visit that is not at the desk named, or a request with the same Idempotency-Key still running. | Read the latest state (GET the visit or desk) and decide again. For an idempotency conflict, wait a moment and retry with the same key. |
409 INVALID_TRANSITION | The visit cannot go there from where it is now — usually because somebody at a desk moved it first. | GET the visit, look at its status, and only send an edge the table on the Visits page allows. |
410 HOST_MOVED | The request went to an old host. The API is served from business.linemeup.in. | Use https://business.linemeup.in/api/v1. |
422 VALIDATION_FAILED | A field is missing, the wrong type or out of range. details names each field by its dotted path, with a sentence about it. | Fix the fields named in details. The reference lists every field's limits. |
429 TOO_MANY_REQUESTS | The key, the business, or this address has made too many requests this minute. | Wait the number of seconds in Retry-After, then retry. See Rate limits. |
500 INTERNAL | Something went wrong on our side. Nothing about the request is to blame. | Retry with backoff. A POST /v1/visits sent with an Idempotency-Key is safe to retry. |
503 NOT_AVAILABLE_YET | The request is fine, but something it needs is not switched on yet. Settings answers this when webhook signing is not configured; the v1 endpoints are not expected to. | Retry later. If it lasts, tell us through Support. |
503 WORKER_UNREACHABLE | The background worker could not be reached, so the request could not be answered. The v1 endpoints do not depend on it and are not expected to answer this; the platform admin's pages do. | Retry later. If it lasts, tell us through Support. |
504 WORKER_TIMEOUT | The background worker did not answer in time. The request may have been applied. The v1 endpoints are not expected to answer this; the platform admin's pages do. | Refresh and check before you send it again, or a retry may do it twice. |
400 BAD_REQUEST #
A write (POST, PUT or PATCH) was sent with a query string. Every field of a write goes in its JSON body; only GET takes query parameters.
Fix: Move the fields from the URL into the body, and send the path with nothing after it.
{
"error": {
"code": "BAD_REQUEST",
"message": "This endpoint takes no query parameters. Send every field in the JSON body"
}
} 401 UNAUTHORIZED #
No key was sent, or the key is not one we know: mistyped, revoked, expired, or made by an owner who has since been removed.
Fix: Check the Authorization header is Bearer and the whole key. If the key was revoked or expired, make a new one.
{
"error": {
"code": "UNAUTHORIZED",
"message": "Invalid API key"
}
} 403 FORBIDDEN #
The key is good but may not do this: it lacks the scope the request needs, it is limited to some lines and tried to make a new one, or the business is the demo.
Fix: Read the message — it names the missing scope. Ask the owner to edit the key's permissions in Settings → Integrations.
{
"error": {
"code": "FORBIDDEN",
"message": "This API key does not have the visits:write permission"
}
} 403 PLAN_LIMIT #
The business's plan does not include API access. Keys and endpoints are kept, but every call is refused until the plan includes them again.
Fix: API access and webhooks come with Basic and every plan above it. The owner can change plan in Settings → Billing.
{
"error": {
"code": "PLAN_LIMIT",
"message": "API keys and webhooks are included in Basic and above.",
"key": "err_plan_feature_integrations",
"params": {
"plan": "Basic"
},
"details": {
"feature": "integrations",
"plan": "free",
"needs": "basic"
}
}
} 404 NOT_FOUND #
There is no such line, desk or visit — or there is, but not one this key may see. The two are deliberately the same answer.
Fix: Check the id. If it is right, the key is limited to other lines or desks; the owner can widen it.
{
"error": {
"code": "NOT_FOUND",
"message": "Visit not found"
}
} 409 CONFLICT #
The request is fine but the business is not in a state to take it: a desk asked to end its shift while it is serving somebody, a visit that is not at the desk named, or a request with the same Idempotency-Key still running.
Fix: Read the latest state (GET the visit or desk) and decide again. For an idempotency conflict, wait a moment and retry with the same key.
{
"error": {
"code": "CONFLICT",
"message": "This visit is not at that desk"
}
} 409 INVALID_TRANSITION #
The visit cannot go there from where it is now — usually because somebody at a desk moved it first.
Fix: GET the visit, look at its status, and only send an edge the table on the Visits page allows.
{
"error": {
"code": "INVALID_TRANSITION",
"message": "This job is already completed and cannot be marked in progress"
}
} 410 HOST_MOVED #
The request went to an old host. The API is served from business.linemeup.in.
Fix: Use https://business.linemeup.in/api/v1.
{
"error": {
"code": "HOST_MOVED",
"message": "This address has moved: use go.linemeup.in or business.linemeup.in"
}
} 422 VALIDATION_FAILED #
A field is missing, the wrong type or out of range. details names each field by its dotted path, with a sentence about it.
Fix: Fix the fields named in details. The reference lists every field's limits.
{
"error": {
"code": "VALIDATION_FAILED",
"message": "Please check the highlighted fields",
"key": "err_please_check_highlighted_fields",
"details": {
"customer.phone": "Enter a phone number — with its country code if it is not Indian"
},
"detailKeys": {
"customer.phone": "err_field_enter_phone_number_country_code_2"
}
}
} 429 TOO_MANY_REQUESTS #
The key, the business, or this address has made too many requests this minute.
Fix: Wait the number of seconds in Retry-After, then retry. See Rate limits.
{
"error": {
"code": "TOO_MANY_REQUESTS",
"message": "Too many requests for this API key. Wait and try again"
}
} 500 INTERNAL #
Something went wrong on our side. Nothing about the request is to blame.
Fix: Retry with backoff. A POST /v1/visits sent with an Idempotency-Key is safe to retry.
{
"error": {
"code": "INTERNAL",
"message": "Something went wrong"
}
} 503 NOT_AVAILABLE_YET #
The request is fine, but something it needs is not switched on yet. Settings answers this when webhook signing is not configured; the v1 endpoints are not expected to.
Fix: Retry later. If it lasts, tell us through Support.
{
"error": {
"code": "NOT_AVAILABLE_YET",
"message": "Webhooks cannot be signed on this server yet. Try again later."
}
} 503 WORKER_UNREACHABLE #
The background worker could not be reached, so the request could not be answered. The v1 endpoints do not depend on it and are not expected to answer this; the platform admin's pages do.
Fix: Retry later. If it lasts, tell us through Support.
{
"error": {
"code": "WORKER_UNREACHABLE",
"message": "The background worker is not reachable"
}
} 504 WORKER_TIMEOUT #
The background worker did not answer in time. The request may have been applied. The v1 endpoints are not expected to answer this; the platform admin's pages do.
Fix: Refresh and check before you send it again, or a retry may do it twice.
{
"error": {
"code": "WORKER_TIMEOUT",
"message": "The background worker did not answer in time; it may have applied the change, so refresh before trying again"
}
}
One more, before a request reaches the API at all: a body that is not valid JSON, or is larger
than 1 MB, is refused with a 400 or a 413 by the web server. Check the
Content-Type and the body you send.
Handling errors#
- Do not retry a
4xxunchanged, except a429, and a409after reading the latest state. A401or403will answer the same until a person changes the key or the plan. - Retry a
5xxor a network failure with backoff — 1 second, then 2, 4, 8, up to a minute — and give up after a few minutes. Reads are always safe to retry. - Retrying a write.
POST /v1/visitswith an Idempotency-Key is safe to retry. Transitions are safe in a different way: the secondcompletedfor a visit already completed is a409, never a second completion — read the visit and carry on. Call next is not safe to retry blindly: a retry after a timeout may call a second person. ReadGET /v1/visits?status=calledfirst. - Treat a
404as final for that id with that key. It is the same answer for "never existed", "another business's" and "outside this key's lines".
async function call(method, path, body) {
const res = await fetch(`https://business.linemeup.in/api/v1${path}`, {
method,
headers: {
Authorization: `Bearer ${process.env.LINEMEUP_API_KEY}`,
"Content-Type": "application/json",
},
body: body === undefined ? undefined : JSON.stringify(body),
});
if (res.ok) return res.json();
const { error } = await res.json();
switch (res.status) {
case 401: // the key is wrong, revoked or expired — stop and tell a person
case 403: // a scope or the plan — retrying will not help
throw new Error(`${error.code}: ${error.message}`);
case 404: // unknown to this key — forget the id, or widen the key
case 409: // the state moved on — read it again, then decide
case 422: // fix the fields in error.details
return { refused: error };
case 429: // wait Retry-After seconds, then try again
await new Promise((r) => setTimeout(r, Number(res.headers.get("Retry-After")) * 1000));
return call(method, path, body);
default: // 5xx — retry with backoff; POST /v1/visits needs an Idempotency-Key for that
throw new Error(`Line Me Up answered ${res.status}`);
}
}