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

# Signing a document

> The end-to-end path: create → route → send → sign → download

Here is the full path from a template to a signed PDF. Every step except downloading
requires the `documents:write` scope; downloading requires `documents:read`.

One principle matters more than the rest: **you produce the signature yourself**. The
server never signs on your behalf — you send a ready base64 CMS/EDS block, created on
the client side with the owner's key, and the server verifies and records it.

<Frame caption="The order of calls: from creation to a signed PDF">
  ```mermaid theme={null}
  %%{init: {'theme':'neutral'}}%%
  sequenceDiagram
      participant C as Your code
      participant K as NCALayer / eGov Mobile
      participant A as Doodocs People API
      C->>A: POST /documents (from template)
      A-->>C: document, status DRAFT
      C->>A: PUT /documents/{id}/route
      A-->>C: route set
      C->>A: POST /documents/{id}/_send
      A-->>C: status ON_APPROVAL / ON_SIGN
      C->>A: GET /documents/{id}
      A-->>C: route.steps[].participants[].id
      C->>K: sign with owner's key
      K-->>C: base64 CMS/EDS
      C->>A: POST /documents/{id}/_sign (participant_id, signature)
      A-->>C: status COMPLETED
      C->>A: GET /documents/{id}/download?file_type=FILE_TYPE_PDF
      A-->>C: temporary link to the PDF
  ```
</Frame>

<Steps>
  <Step title="Create a document from a template">
    `POST /documents` creates a draft from a template. The initiator is set by the server
    as the key owner's employee — it cannot be spoofed.

    ```bash theme={null}
    curl -X POST -H "X-API-Key: ddp_YOUR_KEY" -H "Content-Type: application/json" \
      https://app.doodocs.kz/api/developer/v1/documents \
      -d '{
        "title": "Employment order — Kulyash Baiseitova",
        "type_id": "t1…",
        "template_id": "tpl_7c1a…",
        "template_version": 3,
        "template_values": {
          "employee_full_name": "Kulyash Baiseitova",
          "position": "HR Manager",
          "start_date": "2026-02-10"
        },
        "subject_employee_ids": ["3f2a9c7e-…"]
      }'
    ```

    The response is the created document with status `DOCUMENT_STATUS_DRAFT`. The
    `number`, `date`, and `organization_id` fields can be passed explicitly; otherwise
    they are set by the tenant's rules.
  </Step>

  <Step title="Set the route">
    `PUT /documents/{document_id}/route` sets the approval and signing steps. Each step
    is a `step_type`, a `rule`, and a list of `employee_ids`.

    ```bash theme={null}
    curl -X PUT -H "X-API-Key: ddp_YOUR_KEY" -H "Content-Type: application/json" \
      https://app.doodocs.kz/api/developer/v1/documents/8c2d…/route \
      -d '{
        "steps": [
          { "step_type": "STEP_TYPE_APPROVAL", "rule": "STEP_RULE_ANY_ONE", "employee_ids": ["mgr_ax…"] },
          { "step_type": "STEP_TYPE_SIGNING",  "rule": "STEP_RULE_ALL",     "employee_ids": ["9b7f…"] }
        ]
      }'
    ```

    `step_type` is `STEP_TYPE_APPROVAL` (approval) or `STEP_TYPE_SIGNING` (signing).
    `rule` is `STEP_RULE_ALL` (all participants required) or `STEP_RULE_ANY_ONE` (one is
    enough). The document stays in `DRAFT` — the route is not launched yet. On an
    already-launched document, unpassed steps are edited with
    `POST /documents/{document_id}/route/_modify`.
  </Step>

  <Step title="Send along the route">
    `POST /documents/{document_id}/_send` launches the route: `DRAFT` moves to
    `ON_APPROVAL` or `ON_SIGN`.

    ```bash theme={null}
    curl -X POST -H "X-API-Key: ddp_YOUR_KEY" -H "Content-Type: application/json" \
      https://app.doodocs.kz/api/developer/v1/documents/8c2d…/_send \
      -d '{ "initial_action": "SEND_INITIAL_ACTION_SEND_ONLY" }'
    ```

    `initial_action` defaults to `SEND_INITIAL_ACTION_SEND_ONLY` — just send. The values
    `…_APPROVE_FIRST_STEP` and `…_SIGN_FIRST_STEP` immediately close the first step on
    behalf of the initiator (in which case `signature` and `sign_method` are passed with
    them) and are available only to the initiator.
  </Step>

  <Step title="Find your participant_id">
    Read the document and take the `id` of your active participant from
    `route.steps[].participants[]` — that is what you pass into signing and approval.

    ```bash theme={null}
    curl -H "X-API-Key: ddp_YOUR_KEY" \
      https://app.doodocs.kz/api/developer/v1/documents/8c2d…
    ```

    ```json theme={null}
    {
      "route": {
        "steps": [
          {
            "step_type": "STEP_TYPE_SIGNING",
            "participants": [
              { "id": "p1…", "employee_id": "9b7f…", "status": "PARTICIPANT_STATUS_PENDING" }
            ]
          }
        ]
      }
    }
    ```
  </Step>

  <Step title="Sign">
    `POST /documents/{document_id}/_sign` records the signature. `signature` is a ready
    base64 CMS/EDS block created on the client side (desktop NCALayer or eGov Mobile).
    The server verifies and commits it.

    **What exactly to sign.** The CMS is formed over the content of the
    document's PDF file — the one pinned to the route at send time. Download it
    via `GET /documents/{document_id}/download` (`file_type=FILE_TYPE_PDF`) and
    sign exactly those bytes. In NCALayer this is the
    `createCMSSignatureFromBase64` method with the PDF content in base64. The
    server checks the signature against that file's digests — a CMS over any
    other content is rejected.

    ```bash theme={null}
    curl -X POST -H "X-API-Key: ddp_YOUR_KEY" -H "Content-Type: application/json" \
      https://app.doodocs.kz/api/developer/v1/documents/8c2d…/_sign \
      -d '{
        "participant_id": "p1…",
        "signature": "MIIFx…base64_CMS…",
        "sign_method": "SIGN_METHOD_NCALAYER"
      }'
    ```

    `sign_method` is `SIGN_METHOD_NCALAYER` or `SIGN_METHOD_EGOV_MOBILE`. When the last
    required step is signed, the document moves to `DOCUMENT_STATUS_COMPLETED`.

    <Warning>
      The signature is produced on the client side with the owner's key — the server does
      not create it. The private key and CMS/EDS generation stay with you; only the ready
      base64 block travels to the API. Do not send the private key to the server.
    </Warning>
  </Step>

  <Step title="Download the signed PDF">
    `GET /documents/{document_id}/download` returns a temporary link to the file.
    `file_type` defaults to `FILE_TYPE_PDF` — the signed PDF.

    ```bash theme={null}
    curl -H "X-API-Key: ddp_YOUR_KEY" \
      "https://app.doodocs.kz/api/developer/v1/documents/8c2d…/download?file_type=FILE_TYPE_PDF"
    ```

    ```json theme={null}
    { "url": "https://storage.doodocs.kz/…/signed.pdf?X-Amz-Signature=…" }
    ```

    Other file types — `FILE_TYPE_PREVIEW` (print form), `FILE_TYPE_DDCARD`
    (e-signature card), `FILE_TYPE_DOCX`, `FILE_TYPE_JSON`.
  </Step>
</Steps>

## Approve, reject, request changes, revoke

Besides signing, a participant acts on **their own** active slot — `participant_id` is
taken from the same `route.steps[].participants[].id`.

| Endpoint                                | Action                                                                             |
| --------------------------------------- | ---------------------------------------------------------------------------------- |
| `POST /documents/{id}/_approve`         | Approve the step. Body: `participant_id`, `comment`? (optional)                    |
| `POST /documents/{id}/_reject`          | Reject → `REJECTED`. Body: `participant_id`, `comment` (required)                  |
| `POST /documents/{id}/_request_changes` | Request changes → `CHANGE_REQUESTED`. Body: `participant_id`, `comment` (required) |
| `POST /documents/{id}/_revoke`          | Revoke a document in progress → `REVOKED`. Available to the initiator or a manager |

```bash theme={null}
curl -X POST -H "X-API-Key: ddp_YOUR_KEY" -H "Content-Type: application/json" \
  https://app.doodocs.kz/api/developer/v1/documents/8c2d…/_approve \
  -d '{ "participant_id": "p1…", "comment": "Approved" }'
```

<Note>
  `_approve`, `_reject`, and `_request_changes` act on the caller's active slot. To find
  out which step is active now and what your `participant_id` is, re-read the document
  through `GET /documents/{document_id}`.
</Note>

## Next

<Columns cols={2}>
  <Card title="Templates" icon="file-lines" href="/en/guides/documents/templates">
    How to read `variable_schema` before creating.
  </Card>

  <Card title="Events" icon="bell" href="/en/guides/documents/events">
    Catch `document.completed` instead of polling the status.
  </Card>
</Columns>
