Before you begin
- Store your API key in the
SAJN_API_KEYenvironment variable. To create a key, go to workspace settings in the sajn app, then Utvecklare (Developer) > API-nycklar (API keys). The key sees the documents of its workspace. - Have somewhere to store the newest
updatedAtyou’ve synced, such as a row in your database.
Run an incremental sync
1
Ask for what changed
Filter The example leaves out
GET /api/v1/documents with updatedAfter set to the newest updatedAt you’ve stored, sorted by updatedAt in ascending order:updatedAfter includes its boundary, so the last document of the previous run comes back again. Write each document as an upsert, and the overlap is harmless. On the first run, leave out updatedAfter.The response is similar to the following:documentMeta content. Each list item includes its parties’ signing status and its tags, but not the parties’ full details or the custom fields; get those from Get a document by ID for the documents you need them for. To get the total count, add include=total.2
Walk the pages with the cursor
Pass Cursor pages stay stable while documents change between requests, which page numbers don’t. The cursor follows these rules:
nextCursor back as cursor, with the same filters and sort, until hasMore is false. The following programs run the whole sync and print the timestamp to store for the next run:- It works with every
orderByvalue. Documents without acompletedAtorexpiresAtvalue come last when you sort by that field. - Keep the same
orderBy,orderDirection, and filters for every request in one walk. A cursor from another sort returns400 VALIDATION_FAILED. - Cursors are opaque. Don’t build or edit them.
3
Store the timestamp
After the last page, store the newest
updatedAt you saw. The next run passes it as updatedAfter.Narrow the sync
The list takes the following filters. Send each value as a plain string, not JSON-encoded:completedAfterandcompletedBefore: the completion time, for syncing signed documents only.status: one or more statuses, such asstatus=PENDING,COMPLETED, or a repeated parameter.externalId: the document with exactly thisexternalId, case-sensitive. For a partial match, usequery.templateId: documents created from one or more templates.tagId,folderId, andresponsibleUserId: documents with a tag, in a folder, or with a responsible user. For top-level documents, usefolderId=root.archived:truefor archived documents only. The default,false, leaves archived documents out.
400 VALIDATION_FAILED with the issue code UNRECOGNIZED_KEY, so a misspelled filter fails instead of returning every document. For more information, see Query parameters.
Retry writes safely
APOST /api/v1/documents request that times out might have created the document. Send an Idempotency-Key header on every write, and a retry with the same key and body replays the first response instead of running again:
Handle errors
400 VALIDATION_FAILED: an unknown filter, a malformed date, or a cursor from another sort. Readissues.429 RATE_LIMITED: wait the number of seconds in theRetry-Afterheader, and then continue from the same cursor.400 IDEMPOTENCY_KEY_REUSED: you sent a key again with a different request. Use a new key.409 IDEMPOTENCY_KEY_IN_USE: the first request with the key is still running. Wait and retry.
Next steps
Sync completed documents nightly
Archive every signed PDF on a schedule.
Replay and reconcile
Recover webhook events you missed.
Pagination
Learn how every list paginates.

