> ## Documentation Index
> Fetch the complete documentation index at: https://docs.sajn.se/llms.txt
> Use this file to discover all available pages before exploring further.

# Reminders and expiration

> Set a signing deadline, configure automatic reminders, remind parties on demand, and move the deadline of a sent document

In this guide, you set a deadline on a document, configure the automatic reminders that sajn sends before it, remind parties yourself, and move the deadline after the document is sent.

## Before you begin

* Store your API key in the `SAJN_API_KEY` environment variable. To create a key, go to workspace settings in the sajn app, then **Utvecklare** (Developer) > **API-nycklar** (API keys).
* Have a document. For more information, see [Create a document](/guides/documents/create-document).

## How reminders and expiration work

* `expiresAt` is the signing deadline. If you don't set it, the document gets the workspace's default expiration, if the workspace has one.
* `documentMeta.reminderIntervalDays` is the number of days between automatic reminders, as an integer. `null` uses the default of 3 days, and `0` turns off the recurring reminders. The sajn app offers `1`, `2`, `3`, and `5`.
* Automatic reminders need a deadline. A document without `expiresAt` gets no automatic reminders.
* Each party's first reminder comes `reminderIntervalDays` days after the party is invited. In a sequential document, that's when it becomes the party's turn. If the deadline is more than 90 days away, the reminders start 90 days before it.
* A final reminder goes out the day before the deadline. It's sent even when `reminderIntervalDays` is `0`.
* A party gets at most five recurring reminders. Reminders stop when the party signs, when an address bounces, and at the deadline.
* Automatic reminders go out between 08:00 and 10:00 Swedish time.

## Set the deadline and the reminders

<Steps>
  <Step title="Set them when you create the document">
    Send `expiresAt` and `documentMeta.reminderIntervalDays` in the create request:

    <CodeGroup>
      ```bash curl theme={null}
      curl -X POST https://app.sajn.se/api/v1/documents \
        -H "Authorization: Bearer $SAJN_API_KEY" \
        -H "Sajn-Version: 2026-10" \
        -H "Content-Type: application/json" \
        -d '{
          "name": "Quote - Website project",
          "templateId": "TEMPLATE_ID",
          "expiresAt": "2026-10-31T16:00:00Z",
          "documentMeta": { "reminderIntervalDays": 5 },
          "parties": [
            { "name": "Alex Andersson", "email": "alex@example.com", "role": "SIGNER" }
          ]
        }'
      ```

      ```javascript Node.js theme={null}
      const response = await fetch("https://app.sajn.se/api/v1/documents", {
        method: "POST",
        headers: {
          Authorization: `Bearer ${process.env.SAJN_API_KEY}`,
          "Sajn-Version": "2026-10",
          "Content-Type": "application/json",
        },
        body: JSON.stringify({
          name: "Quote - Website project",
          templateId: "TEMPLATE_ID",
          expiresAt: "2026-10-31T16:00:00Z",
          documentMeta: { reminderIntervalDays: 5 },
          parties: [{ name: "Alex Andersson", email: "alex@example.com", role: "SIGNER" }],
        }),
      });
      const document = await response.json();
      console.log(document.expiresAt, document.documentMeta.reminderIntervalDays);
      ```

      ```python Python theme={null}
      import os

      import requests

      response = requests.post(
          "https://app.sajn.se/api/v1/documents",
          headers={
              "Authorization": f"Bearer {os.environ['SAJN_API_KEY']}",
              "Sajn-Version": "2026-10",
          },
          json={
              "name": "Quote - Website project",
              "templateId": "TEMPLATE_ID",
              "expiresAt": "2026-10-31T16:00:00Z",
              "documentMeta": {"reminderIntervalDays": 5},
              "parties": [{"name": "Alex Andersson", "email": "alex@example.com", "role": "SIGNER"}],
          },
      )
      response.raise_for_status()
      document = response.json()
      print(document["expiresAt"], document["documentMeta"]["reminderIntervalDays"])
      ```
    </CodeGroup>

    Replace `TEMPLATE_ID` with the ID of a template. The response is the document, with the deadline in `expiresAt` and the interval in `documentMeta.reminderIntervalDays`.

    `expiresAt` is an ISO 8601 date-time. Without an offset, it's UTC.
  </Step>

  <Step title="Change them on a draft">
    To change the deadline or the interval before you send, send a `PATCH` request:

    ```bash theme={null}
    curl -X PATCH https://app.sajn.se/api/v1/documents/DOCUMENT_ID \
      -H "Authorization: Bearer $SAJN_API_KEY" \
      -H "Sajn-Version: 2026-10" \
      -H "Content-Type: application/json" \
      -d '{
        "expiresAt": "2026-11-15T16:00:00Z",
        "documentMeta": { "reminderIntervalDays": 0 }
      }'
    ```

    Replace `DOCUMENT_ID` with the document ID. You can also change `reminderIntervalDays` after sending, until the document is completed. sajn reschedules the reminders of every party who can still be reminded.
  </Step>
</Steps>

## Remind parties on demand

To remind parties who haven't signed, send a `POST` request to `/api/v1/documents/DOCUMENT_ID/reminders`. Without a body, every eligible party is reminded. To remind specific parties, list them in `partyIds`:

<CodeGroup>
  ```bash curl theme={null}
  curl -X POST https://app.sajn.se/api/v1/documents/DOCUMENT_ID/reminders \
    -H "Authorization: Bearer $SAJN_API_KEY" \
    -H "Sajn-Version: 2026-10" \
    -H "Content-Type: application/json" \
    -d '{ "partyIds": ["PARTY_ID"] }'
  ```

  ```javascript Node.js theme={null}
  const documentId = "DOCUMENT_ID";

  const response = await fetch(
    `https://app.sajn.se/api/v1/documents/${documentId}/reminders`,
    {
      method: "POST",
      headers: {
        Authorization: `Bearer ${process.env.SAJN_API_KEY}`,
        "Sajn-Version": "2026-10",
        "Content-Type": "application/json",
      },
      body: JSON.stringify({ partyIds: ["PARTY_ID"] }),
    },
  );
  const { results } = await response.json();
  for (const result of results) {
    console.log(result.partyName, result.status, result.reason);
  }
  ```

  ```python Python theme={null}
  import os

  import requests

  document_id = "DOCUMENT_ID"

  response = requests.post(
      f"https://app.sajn.se/api/v1/documents/{document_id}/reminders",
      headers={
          "Authorization": f"Bearer {os.environ['SAJN_API_KEY']}",
          "Sajn-Version": "2026-10",
      },
      json={"partyIds": ["PARTY_ID"]},
  )
  response.raise_for_status()
  for result in response.json()["results"]:
      print(result["partyName"], result["status"], result["reason"])
  ```
</CodeGroup>

Replace the following:

* `DOCUMENT_ID`: the ID of a `PENDING` document.
* `PARTY_ID`: the ID of a party who hasn't signed.

The response reports the outcome for each party:

```json theme={null}
{
  "sent": 1,
  "skipped": 0,
  "failed": 0,
  "results": [
    {
      "partyId": "cm4k2xa3f0002abcd5678ijkl",
      "partyEmail": "alex@example.com",
      "partyName": "Alex Andersson",
      "status": "SENT",
      "reason": null
    }
  ]
}
```

`status` is `SENT`, `SKIPPED`, or `FAILED`. A party can get one reminder per 24 hours, so a second reminder within that window is `SKIPPED` with a `reason`. A skipped party doesn't fail the request.

Each reminder goes out on the party's delivery method. To choose the channel, set `channel` to `EMAIL` or `SMS`. A party without an address for the channel is skipped with the reason `MISSING_EMAIL` or `MISSING_PHONE`. SMS reminders are billed like SMS invitations.

## Move the deadline of a sent document

To change the deadline of a `PENDING` or `EXPIRED` document, send a `POST` request to `/api/v1/documents/DOCUMENT_ID/extend-expiration`:

```bash theme={null}
curl -X POST https://app.sajn.se/api/v1/documents/DOCUMENT_ID/extend-expiration \
  -H "Authorization: Bearer $SAJN_API_KEY" \
  -H "Sajn-Version: 2026-10" \
  -H "Content-Type: application/json" \
  -d '{ "expiresAt": "2026-11-30T16:00:00Z" }'
```

The new date must be at least 24 hours in the future, and it can be earlier or later than the current one. sajn updates `expiresAt`, records the change in the audit log, and emails every party who can still be notified that the deadline moved. An `EXPIRED` document returns to `PENDING`. The response is the document, and the request needs the `EDIT_DOCUMENT` workspace permission.

## What happens at the deadline

When the deadline passes, the document's status changes from `PENDING` to `EXPIRED`, parties can no longer sign, and the `document.expired` webhook event fires.

To keep a document signable after its deadline, set `documentMeta.signableAfterExpired` to `true`. The document then stays `PENDING`, and `document.expired` still fires. Automatic reminders stop at the deadline either way.

To react to an expired document, handle the `document.expired` event. Its `data.object` is the document, so you can find the parties who didn't sign:

```javascript theme={null}
if (event.type === "document.expired") {
  const document = event.data.object;
  const unsigned = document.parties.filter(
    (party) => party.role === "SIGNER" && party.signingStatus === "NOT_SIGNED",
  );
  console.log(`${document.name} expired; ${unsigned.length} unsigned`);
}
```

Verify the signature before you handle the event, as described in [Verify signatures](/webhooks/verify-signatures). For the payload, see [Webhook payloads](/webhooks/payloads).

## Handle errors

* `400 VALIDATION_FAILED` on `extend-expiration`: the new date is less than 24 hours away, or the document isn't `PENDING` or `EXPIRED`. For a `DRAFT`, use `PATCH` instead.
* `403 PERMISSION_DENIED` on `extend-expiration`: you lack the `EDIT_DOCUMENT` permission.

For the error shape and every code, see [Errors](/api-fundamentals/errors).

## Next steps

<CardGroup cols={2}>
  <Card title="Multi-party signing" icon="users" href="/guides/documents/multi-party-signing">
    Keep a signing sequence moving.
  </Card>

  <Card title="Webhooks" icon="webhook" href="/webhooks/overview">
    Get notified when a document expires.
  </Card>

  <Card title="Send reminders" icon="code" href="/api-reference/send-reminders-to-one-or-more-signers">
    See the reminders endpoint in the API reference.
  </Card>

  <Card title="Change the expiration date" icon="code" href="/api-reference/change-a-sent-documents-expiration-date">
    See the expiration endpoint in the API reference.
  </Card>
</CardGroup>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.