Using the API
Idempotency
A retried request must not add a second visit.
Networks fail in the worst place: after we added the visit and before you heard back. Retry
blindly and the person is in the line twice. Send an Idempotency-Key and a retry is answered
with the visit the first request made.
The Idempotency-Key header#
POST /v1/visits honours it. Every other write is safe to repeat by its nature — a second
completed is a 409, never a second completion — except Call next,
which is not; see Handling errors.
-
Any string of 1 to 255 characters that is unique per visit you mean to add: a UUID, or your
own record's id such as
crm-visit-1042. - Make it once, before the first try, and send the same one on every retry.
- Keys belong to the business, not to the API key, so a retry still works after rotating keys.
curl
curl -X POST "https://business.linemeup.in/api/v1/visits" \
-H "Authorization: Bearer $LINEMEUP_API_KEY" \
-H "Idempotency-Key: crm-visit-1042" \
-H "Content-Type: application/json" \
-d '{"lineId":"line-0193f2a1-7c4e-7a10-9b2e-000000000001","customer":{"name":"Asha Rao","phone":"+919000000001"},"note":"First visit","reference":"CRM-1042","consent":true}' Node
const res = await fetch("https://business.linemeup.in/api/v1/visits", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.LINEMEUP_API_KEY}`,
"Idempotency-Key": "crm-visit-1042",
"Content-Type": "application/json",
},
body: JSON.stringify({
"lineId": "line-0193f2a1-7c4e-7a10-9b2e-000000000001",
"customer": {
"name": "Asha Rao",
"phone": "+919000000001"
},
"note": "First visit",
"reference": "CRM-1042",
"consent": true
}),
});
if (!res.ok) throw new Error(`Line Me Up answered ${res.status}`);
const answer = await res.json(); Python
import os
import requests
res = requests.post(
"https://business.linemeup.in/api/v1/visits",
headers={
"Authorization": f"Bearer {os.environ['LINEMEUP_API_KEY']}",
"Idempotency-Key": "crm-visit-1042",
},
json={
"lineId": "line-0193f2a1-7c4e-7a10-9b2e-000000000001",
"customer": {
"name": "Asha Rao",
"phone": "+919000000001",
},
"note": "First visit",
"reference": "CRM-1042",
"consent": True,
},
timeout=10,
)
res.raise_for_status()
answer = res.json() PHP
<?php
$ch = curl_init("https://business.linemeup.in/api/v1/visits");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_HTTPHEADER => [
"Authorization: Bearer " . getenv("LINEMEUP_API_KEY"),
"Idempotency-Key: crm-visit-1042",
"Content-Type: application/json",
],
CURLOPT_POSTFIELDS => json_encode([
"lineId" => 'line-0193f2a1-7c4e-7a10-9b2e-000000000001',
"customer" => [
"name" => 'Asha Rao',
"phone" => '+919000000001',
],
"note" => 'First visit',
"reference" => 'CRM-1042',
"consent" => true,
]),
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 10,
]);
$answer = json_decode(curl_exec($ch), true); What happens when a key is sent again:
| When | The answer |
|---|---|
| The first request succeeded; same body |
The first answer again, word for word, with Idempotent-Replayed: true. No
new visit.
|
| The first request is still running | 409 CONFLICT. Wait a second and retry with the same key. (In the rare case
the visit was added but its answer could not be kept, the key goes on answering
409 until it is forgotten, rather than add a second visit. Look for it with
GET /v1/visits.)
|
| The first request was refused or failed | Nothing is remembered: the retry is a fresh attempt. |
| The same key with a different body | 422 VALIDATION_FAILED, and nothing is added. Use a new key for a new
visit. |
"The same body" means the same fields with the same values — the order you write them in does not matter.
HTTP/1.1 200 OK
Idempotent-Replayed: true
Content-Type: application/json; charset=utf-8 {
"data": [
{
"id": "job-0193f2a1-7c4e-7a10-9b2e-000000000001",
"journeyId": "jny-0193f2a1-7c4e-7a10-9b2e-000000000001",
"token": "GEN-20261030-014",
"lineId": "line-0193f2a1-7c4e-7a10-9b2e-000000000001",
"status": "queued",
"bookingType": "walk_in",
"priority": false,
"note": "First visit",
"customer": {
"name": "Asha Rao",
"phone": "+919000000001"
},
"services": [
{
"id": "svc-0193f2a1-7c4e-7a10-9b2e-000000000002",
"name": "General consultation"
}
],
"deskId": null,
"position": 3,
"slotAt": null,
"createdAt": "2026-10-30T04:12:00.000Z",
"calledAt": null,
"startedAt": null,
"endedAt": null,
"updatedAt": "2026-10-30T04:12:00.000Z"
}
]
} {
"error": {
"code": "VALIDATION_FAILED",
"message": "This Idempotency-Key was sent before with a different request. Use a new key for a new request",
"details": {
"idempotencyKey": "reused"
}
}
} {
"error": {
"code": "CONFLICT",
"message": "A request with this Idempotency-Key is still being processed. Retry it shortly"
}
} Retry-safe examples#
Retry on a timeout, a dropped connection, a 409, a 429 or a
5xx — with the same key, and a growing wait. Anything else is an answer.
Node
import { randomUUID } from "node:crypto";
// One key per visit you mean to add — made once, reused on every retry.
async function addVisit(visit, key = randomUUID()) {
for (let attempt = 1; attempt <= 5; attempt++) {
try {
const res = await fetch("https://business.linemeup.in/api/v1/visits", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.LINEMEUP_API_KEY}`,
"Content-Type": "application/json",
"Idempotency-Key": key,
},
body: JSON.stringify(visit),
signal: AbortSignal.timeout(10_000),
});
// 409: the first try is still running. 429 and 5xx: try again shortly.
if (res.status !== 409 && res.status !== 429 && res.status < 500) {
const answer = await res.json();
if (!res.ok) throw new Error(answer.error.message);
return answer.data;
}
} catch (error) {
if (error.name !== "TimeoutError" && error.name !== "TypeError") throw error;
}
await new Promise((resolve) => setTimeout(resolve, 2 ** attempt * 500));
}
throw new Error("Line Me Up did not answer; the same key is safe to try later");
} Python
import os
import time
import uuid
import requests
def add_visit(visit, key=None):
key = key or str(uuid.uuid4()) # one key per visit, reused on every retry
for attempt in range(1, 6):
try:
res = requests.post(
"https://business.linemeup.in/api/v1/visits",
headers={
"Authorization": f"Bearer {os.environ['LINEMEUP_API_KEY']}",
"Idempotency-Key": key,
},
json=visit,
timeout=10,
)
if res.status_code not in (409, 429) and res.status_code < 500:
res.raise_for_status()
return res.json()["data"]
except (requests.Timeout, requests.ConnectionError):
pass
time.sleep(2 ** attempt * 0.5)
raise RuntimeError("Line Me Up did not answer; the same key is safe to try later") PHP
<?php
function add_visit(array $visit, ?string $key = null): array
{
$key = $key ?? bin2hex(random_bytes(16)); // one key per visit, reused on every retry
for ($attempt = 1; $attempt <= 5; $attempt++) {
$ch = curl_init('https://business.linemeup.in/api/v1/visits');
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . getenv('LINEMEUP_API_KEY'),
'Content-Type: application/json',
'Idempotency-Key: ' . $key,
],
CURLOPT_POSTFIELDS => json_encode($visit),
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 10,
]);
$body = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
if ($body !== false && $status !== 409 && $status !== 429 && $status < 500) {
$answer = json_decode($body, true);
if ($status !== 200) {
throw new RuntimeException($answer['error']['message']);
}
return $answer['data'];
}
usleep((2 ** $attempt) * 500000);
}
throw new RuntimeException('Line Me Up did not answer; the same key is safe to try later');
} How long a key is remembered#
24 hours from the first request. After that the key is forgotten, and sending it again adds a
new visit — so a retry belongs within the day, and a key should never be reused on purpose. If
you take your keys from your own records, prefix them with the date or the system (web-2026-…) so two systems cannot collide.