Using the API
Requests and responses
How every request and response is shaped.
JSON in, JSON out#
-
Send bodies as JSON with
Content-Type: application/json, at most 1 MB. AGETtakes its filters in the query string and has no body. A write —POST,PUT,PATCH— takes every field in its body and no query string at all: one is refused with a400BAD_REQUEST. -
Every answer is JSON. A successful one is
200; a refused one has the error body. -
A list comes wrapped —
{ "data": [ … ] }— so it can grow a field such asnextCursorwithout breaking anybody. -
Every field a resource has is always in the answer. When it has no value it is
null, never missing: a waiting visit'sdeskIdisnull, not absent. - Ignore fields you do not know. Within v1 we add fields; a client that refuses an answer with one more field than it expected will break on a day we announced as safe.
-
PUTreplaces: send every setting, asPUT /v1/linesdescribes.PATCHchanges only the fields you send. A field you send asnullclears it, where the field allows null.
Ids#
Every id is a short prefix that says what it is, a hyphen, and a UUID — for example
line-0193f2a1-7c4e-7a10-9b2e-000000000001. Treat ids as opaque strings up to 64 characters; compare them
exactly, and never build one.
| Prefix | Names |
|---|---|
bus- | A business |
line- | A line |
svc- | A service, or a heading on the menu |
sta- | A desk |
job- | A visit |
jny- | A journey — the visits one person made in one go, across lines |
evt- | A webhook event |
whd- | One delivery of an event to one endpoint |
A visit's token — the number on the customer's screen and the desk's board — is for people,
not for code. It is unique on its line for its day and repeats on other days. Find a visit by its
id.
Timestamps and time zones#
Every moment is an ISO 8601 string in UTC, to the millisecond:
2026-10-30T04:31:00.000Z. When you send one (a break's backAt, a
sync's updatedSince) you may give any offset — +05:30 is fine — and it is
read as that moment.
Show times to people in the business's own zone, not your server's: GET /v1/business
answers its timeZone (an IANA name such as Asia/Kolkata) and
its clockFormat, 12h or 24h, which is how its staff
expect to read a time.
A business's day#
"Today" is the business's today, in its time zone — never UTC's and never your server's. A clinic in Kolkata that opens at 7 am is open on a date for which UTC still says yesterday.
GET /v1/businessanswerstodayasYYYY-MM-DD.-
fromandtoonGET /v1/visitsare the business's dates, and both are included:from=to=todayis one whole day. - A visit belongs to the day of its booking slot when it has one, and otherwise to the day it was added.
{
"id": "bus-0193f2a1-7c4e-7a10-9b2e-000000000001",
"name": "Sunrise Clinic",
"slug": "sunrise-clinic",
"category": "healthcare",
"timeZone": "Asia/Kolkata",
"today": "2026-10-30",
"clockFormat": "12h"
} Pagination with cursors#
GET /v1/visits answers a page at a time, newest first: by the booking slot, or for a
walk-in by when it was added. Ask for up to 100 with limit (50 if you do not say). Each
page carries nextCursor; send it back as cursor for the next page. On the
last page it is null.
curl
curl "https://business.linemeup.in/api/v1/visits?lineId=line-0193f2a1-7c4e-7a10-9b2e-000000000001&from=2026-10-30&to=2026-10-30&limit=2" \
-H "Authorization: Bearer $LINEMEUP_API_KEY" Node
const res = await fetch("https://business.linemeup.in/api/v1/visits?lineId=line-0193f2a1-7c4e-7a10-9b2e-000000000001&from=2026-10-30&to=2026-10-30&limit=2", {
headers: {
Authorization: `Bearer ${process.env.LINEMEUP_API_KEY}`,
},
});
if (!res.ok) throw new Error(`Line Me Up answered ${res.status}`);
const answer = await res.json(); Python
import os
import requests
res = requests.get(
"https://business.linemeup.in/api/v1/visits?lineId=line-0193f2a1-7c4e-7a10-9b2e-000000000001&from=2026-10-30&to=2026-10-30&limit=2",
headers={
"Authorization": f"Bearer {os.environ['LINEMEUP_API_KEY']}",
},
timeout=10,
)
res.raise_for_status()
answer = res.json() PHP
<?php
$ch = curl_init("https://business.linemeup.in/api/v1/visits?lineId=line-0193f2a1-7c4e-7a10-9b2e-000000000001&from=2026-10-30&to=2026-10-30&limit=2");
curl_setopt_array($ch, [
CURLOPT_HTTPHEADER => [
"Authorization: Bearer " . getenv("LINEMEUP_API_KEY"),
],
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 10,
]);
$answer = json_decode(curl_exec($ch), true); {
"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"
},
{
"id": "job-0193f2a1-7c4e-7a10-9b2e-000000000004",
"journeyId": "jny-0193f2a1-7c4e-7a10-9b2e-000000000004",
"token": "GEN-20261030-009",
"lineId": "line-0193f2a1-7c4e-7a10-9b2e-000000000001",
"status": "completed",
"bookingType": "walk_in",
"priority": false,
"note": null,
"customer": {
"name": null,
"phone": null
},
"services": [
{
"id": "svc-0193f2a1-7c4e-7a10-9b2e-000000000002",
"name": "General consultation"
}
],
"deskId": "sta-0193f2a1-7c4e-7a10-9b2e-000000000001",
"position": null,
"slotAt": null,
"createdAt": "2026-10-30T03:40:00.000Z",
"calledAt": "2026-10-30T03:58:00.000Z",
"startedAt": "2026-10-30T03:59:00.000Z",
"endedAt": "2026-10-30T04:10:00.000Z",
"updatedAt": "2026-10-30T04:10:00.000Z"
}
],
"nextCursor": "MjAyNi0xMC0yNVQwNDoxMDowMC4wMDAwMDBafGpvYi0wMDA0"
} curl
curl "https://business.linemeup.in/api/v1/visits?cursor=MjAyNi0xMC0yNVQwNDoxMDowMC4wMDAwMDBafGpvYi0wMDA0&limit=2" \
-H "Authorization: Bearer $LINEMEUP_API_KEY" Node
const res = await fetch("https://business.linemeup.in/api/v1/visits?cursor=MjAyNi0xMC0yNVQwNDoxMDowMC4wMDAwMDBafGpvYi0wMDA0&limit=2", {
headers: {
Authorization: `Bearer ${process.env.LINEMEUP_API_KEY}`,
},
});
if (!res.ok) throw new Error(`Line Me Up answered ${res.status}`);
const answer = await res.json(); Python
import os
import requests
res = requests.get(
"https://business.linemeup.in/api/v1/visits?cursor=MjAyNi0xMC0yNVQwNDoxMDowMC4wMDAwMDBafGpvYi0wMDA0&limit=2",
headers={
"Authorization": f"Bearer {os.environ['LINEMEUP_API_KEY']}",
},
timeout=10,
)
res.raise_for_status()
answer = res.json() PHP
<?php
$ch = curl_init("https://business.linemeup.in/api/v1/visits?cursor=MjAyNi0xMC0yNVQwNDoxMDowMC4wMDAwMDBafGpvYi0wMDA0&limit=2");
curl_setopt_array($ch, [
CURLOPT_HTTPHEADER => [
"Authorization: Bearer " . getenv("LINEMEUP_API_KEY"),
],
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 10,
]);
$answer = json_decode(curl_exec($ch), true); {
"data": [],
"nextCursor": null
}
A cursor is opaque. Send it back exactly as it came, with the same filters; do not decode, build
or store it for long. A cursor we cannot read is a 422. A visit added while you
page is newer than your first page, so it is not on a later one; start again from the first
page, or use updatedSince, to pick it up.
// Every visit of the business's today, page by page.
// get() sends the key and returns the JSON — the Guides have a complete one.
const { today } = await get("/business");
const visits = [];
let cursor = null;
do {
const query = new URLSearchParams({ from: today, to: today, limit: "100" });
if (cursor) query.set("cursor", cursor);
const page = await get(`/visits?${query}`);
visits.push(...page.data);
cursor = page.nextCursor;
} while (cursor !== null); Filtering#
Every filter on GET /v1/visits is optional, and they combine with AND.
| Parameter | Keeps visits |
|---|---|
status |
In any of these statuses: booked, queued, called,
in_progress, completed, cancelled,
no_show.
|
lineId | On any of these lines. A line the key may not see matches nothing. |
from, to | On these business days, both included, YYYY-MM-DD. |
updatedSince | Changed at or after this moment — the filter for a sync. |
limit, cursor | The page size, 1 to 100, and the page. |
A filter that takes several values takes them repeated, or comma-separated:
GET /v1/visits?status=queued&status=called
GET /v1/visits?status=queued,called
For a sync, remember the time you started your last read and pass it as
updatedSince next time — not the time you finished, which would skip whatever changed
while you read.
curl
curl "https://business.linemeup.in/api/v1/visits?updatedSince=2026-10-30T04%3A00%3A00.000Z&limit=100" \
-H "Authorization: Bearer $LINEMEUP_API_KEY" Node
const res = await fetch("https://business.linemeup.in/api/v1/visits?updatedSince=2026-10-30T04%3A00%3A00.000Z&limit=100", {
headers: {
Authorization: `Bearer ${process.env.LINEMEUP_API_KEY}`,
},
});
if (!res.ok) throw new Error(`Line Me Up answered ${res.status}`);
const answer = await res.json(); Python
import os
import requests
res = requests.get(
"https://business.linemeup.in/api/v1/visits?updatedSince=2026-10-30T04%3A00%3A00.000Z&limit=100",
headers={
"Authorization": f"Bearer {os.environ['LINEMEUP_API_KEY']}",
},
timeout=10,
)
res.raise_for_status()
answer = res.json() PHP
<?php
$ch = curl_init("https://business.linemeup.in/api/v1/visits?updatedSince=2026-10-30T04%3A00%3A00.000Z&limit=100");
curl_setopt_array($ch, [
CURLOPT_HTTPHEADER => [
"Authorization: Bearer " . getenv("LINEMEUP_API_KEY"),
],
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 10,
]);
$answer = json_decode(curl_exec($ch), true); {
"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": "completed",
"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": "sta-0193f2a1-7c4e-7a10-9b2e-000000000001",
"position": null,
"slotAt": null,
"createdAt": "2026-10-30T04:12:00.000Z",
"calledAt": "2026-10-30T04:31:00.000Z",
"startedAt": "2026-10-30T04:32:00.000Z",
"endedAt": "2026-10-30T04:47:00.000Z",
"updatedAt": "2026-10-30T04:47:00.000Z"
},
{
"id": "job-0193f2a1-7c4e-7a10-9b2e-000000000004",
"journeyId": "jny-0193f2a1-7c4e-7a10-9b2e-000000000004",
"token": "GEN-20261030-009",
"lineId": "line-0193f2a1-7c4e-7a10-9b2e-000000000001",
"status": "completed",
"bookingType": "walk_in",
"priority": false,
"note": null,
"customer": {
"name": null,
"phone": null
},
"services": [
{
"id": "svc-0193f2a1-7c4e-7a10-9b2e-000000000002",
"name": "General consultation"
}
],
"deskId": "sta-0193f2a1-7c4e-7a10-9b2e-000000000001",
"position": null,
"slotAt": null,
"createdAt": "2026-10-30T03:40:00.000Z",
"calledAt": "2026-10-30T03:58:00.000Z",
"startedAt": "2026-10-30T03:59:00.000Z",
"endedAt": "2026-10-30T04:10:00.000Z",
"updatedAt": "2026-10-30T04:10:00.000Z"
}
],
"nextCursor": null
}