Core concepts
Pagination
Paginated list endpoints return a plain JSON array and put the pointer to the next page in response headers.
Paginated endpoints#
| Endpoint | Parameters |
|---|---|
GET /api/v1/documents | limit (1-100, default 50), cursor, legacy offset |
GET /api/v1/webhooks/{webhook_id}/deliveries | limit (1-100, default 50), cursor, legacy offset |
GET /api/v1/flow-runs | limit (1-200), filters flow_id and status; no cursor |
Other collections (/templates, /webhooks, /flows, /template-catalog) are returned in full.
How it works#
- Request the first page with a
limit. - If more results may exist, the response carries
X-Next-Cursor: <cursor>and aLinkheader withrel="next". - Request the next page with
cursor=<cursor>(keep the samelimit). When neither header is present you have reached the last page.
HTTP/1.1 200 OK
Content-Type: application/json
X-Next-Cursor: bzo1MA
Link: </api/v1/documents?limit=50&cursor=bzo1MA>; rel="next"The Link target is a path relative to the API host; it keeps your other query parameters and replaces cursor/offset.
Cursors are opaque strings: do not build or parse them. A malformed cursor returns 422 with code invalid_cursor. offset keeps working for existing integrations but is ignored when cursor is set. A full last page may still return a cursor, in which case the following request returns an empty array.
Loop through all pages#
url="https://api.sign.yt/v1/documents?limit=100"
while [ -n "$url" ]; do
curl -sS -D headers.txt -o page.json "$url" -H "Authorization: Bearer $SIGNYT_API_KEY"
jq -r '.[] | [.id, .status, .title] | @tsv' page.json
cursor=$(grep -i '^x-next-cursor:' headers.txt | awk '{print $2}' | tr -d '\r')
url=${cursor:+"https://api.sign.yt/v1/documents?limit=100&cursor=$cursor"}
doneimport os
import httpx
headers = {"Authorization": f"Bearer {os.environ['SIGNYT_API_KEY']}"}
params = {"limit": 100}
with httpx.Client(base_url="https://api.sign.yt/v1", headers=headers) as http:
while True:
resp = http.get("/documents", params=params)
resp.raise_for_status()
for doc in resp.json():
print(doc["id"], doc["status"], doc["title"])
cursor = resp.headers.get("X-Next-Cursor")
if not cursor:
break
params = {"limit": 100, "cursor": cursor}const base = "https://api.sign.yt/v1/documents";
const headers = { Authorization: `Bearer ${process.env.SIGNYT_API_KEY}` };
let cursor: string | null = null;
do {
const url = new URL(base);
url.searchParams.set("limit", "100");
if (cursor) url.searchParams.set("cursor", cursor);
const res = await fetch(url, { headers });
if (!res.ok) throw new Error(`HTTP ${res.status}`);
for (const doc of (await res.json()) as { id: string; status: string; title: string }[]) {
console.log(doc.id, doc.status, doc.title);
}
cursor = res.headers.get("X-Next-Cursor");
} while (cursor);The SDKs' documents.list() methods currently take limit and offset; use raw HTTP as above for cursor iteration.
Ordering and consistency#
Pages are cut from the same ordered list on every request, so documents created while you paginate can shift items between pages. For a consistent export, record the IDs you have seen and de-duplicate, or use webhooks to track changes instead of repeated full listings.