Before you begin
-
Python 3.9 or later, and the
requestspackage:pip install requests. -
An API key in the
SAJN_API_KEYenvironment variable. To create one, go to workspace settings in the sajn app, then Utvecklare (Developer) > API-nycklar (API keys). -
A template with a party named
Employeeand FORM fields with the keysemployee-nameandstart-date. Store its ID inSAJN_TEMPLATE_ID. For more information, see Create documents from templates. -
A CSV file named
employees.csvwith a header row:
Build the script
1
Look up the keys once
Every document from the template has the same keys. To check them, create one draft in the sajn app or through the API, and list its values:Replace
DOCUMENT_ID with the draft’s ID. The response lists the values in data. Use the key of each value with filledBy set to SENDER.2
Write the script
Save the following file as The script works as follows:
bulk_send.py. For each row, it finds an existing document by externalId, or creates one, fills in the values, sets the party, and sends it:externalIdties each document to a row, so a second run finds the document instead of creating another one. TheexternalIdfilter matches the whole value.- The create request and each item in the list return the document with its
parties, so the script finds the employee’s party without another request. - The idempotency keys make a retried create or send replay the first response.
- A value that isn’t written doesn’t fail the request. The script checks
successon each item inresults. callwaits for the number of seconds inRetry-Afterwhen the API returns429.
3
Run it
Run the script:The output is similar to the following:Run it again, and every row reports
already sent.Handle errors
403 LIMIT_EXCEEDEDon create: the organization reached its hourly limit for created documents, or its monthly limit for sent documents. Wait, and run the script again; it continues where it stopped.429 RATE_LIMITED: the script waits and retries. For the limits per plan, see Rate limits and quotas.values not written: each failed result has anerrorwith acode.NOT_FOUNDmeans the key doesn’t exist on the template, andVALIDATION_FAILEDmeans the value doesn’t match the field’s type. Fix the CSV or the template.

