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

# Send a document for signing

> End-to-end tutorial: create a package with its stakeholders, upload a document, set the signing process, and activate the workflow.

This tutorial walks you through the complete lifecycle of a signing package using the NSEV
WebPortal API - from creation to activation. By the end, you will have a package ready for a signer
to complete, with a second stakeholder receiving the fully signed document.

This is the **recommended integration flow**, building the package step by step. If you just want
the fastest path to a signed document, see [Instant package](/docs/nsev/build/tutorials/instant-package),
which creates everything in a single call.

## Prerequisites

* A valid access token or Basic Auth credentials - see [Platform Access](/docs/nsev/getting-started/basic-setup)
* Your tenant's base URL set as `BASE_URL`
* A PDF document to upload (max 30 MB)

All examples use `Bearer` authentication. Replace the token and variable values with your own.

***

## Overview

A signing package moves through these steps before a signer can act on it:

1. **Create** the package together with its stakeholders (status: `Draft`)
2. **Upload** one or more documents
3. **Set the process** - a signing step for the signer, followed by a receiver step
4. **Activate** by setting status to `pending`
5. **Monitor** status until the package is `Finished`

***

## Step 1: Create the package with its stakeholders

Create a package in `Draft` status. The package acts as a container - you build it up before
activating the signing flow. Stakeholders are the people involved in the package, and you can
create them directly in the same call. Here we add two:

* **Jane Smith**, who will sign the document
* **John Doe**, who will receive a copy of the fully signed document

What each stakeholder actually *does* is not decided yet - that happens when you set the
process in [Step 3](#step-3-set-the-process).

```bash theme={null}
curl -X POST "$BASE_URL/packages" \
  -H "Authorization: Bearer {access_token}" \
  -H "Content-Type: application/json" \
  -H "User-Agent: MyApp/1.0" \
  -d '{
    "Name": "Employment Contract",
    "Initiator": "sender@yourcompany.com",
    "Stakeholders": [
      {
        "Type": "Person",
        "Language": "en",
        "FirstName": "Jane",
        "LastName": "Smith",
        "EmailAddress": "jane.smith@example.com",
        "BirthDate": "1985-04-12"
      },
      {
        "Type": "Person",
        "Language": "en",
        "FirstName": "John",
        "LastName": "Doe",
        "EmailAddress": "john.doe@example.com",
        "BirthDate": "1979-11-30"
      }
    ]
  }'
```

Save the package `Id` and both stakeholder `Id`s from the response - you will need them in the
next steps.

```json theme={null}
{
  "Id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "Name": "Employment Contract",
  "Status": "Draft",
  "Initiator": "sender@yourcompany.com",
  "Stakeholders": [
    {
      "Type": "Person",
      "Id": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
      "PackageId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
      "Language": "en",
      "FirstName": "Jane",
      "LastName": "Smith",
      "EmailAddress": "jane.smith@example.com"
    },
    {
      "Type": "Person",
      "Id": "b1a271e3-0f43-4a0e-9a72-6d97c1a3c2d4",
      "PackageId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
      "Language": "en",
      "FirstName": "John",
      "LastName": "Doe",
      "EmailAddress": "john.doe@example.com"
    }
  ]
}
```

```bash theme={null}
export PACKAGE_ID="3fa85f64-5717-4562-b3fc-2c963f66afa6"
export SIGNER_STAKEHOLDER_ID="7c9e6679-7425-40de-944b-e07fc1f90ae7"
export RECEIVER_STAKEHOLDER_ID="b1a271e3-0f43-4a0e-9a72-6d97c1a3c2d4"
```

<Note>
  You can also add stakeholders to an existing draft package later with
  `POST /packages/{packageId}/stakeholders` - useful when the participants are not known at
  creation time.
</Note>

### Get notified instead of polling

For a real integration, set a `CallBackUrl` when you create the package. NSEV then sends your
system an HTTP `POST` each time the package's status changes, so you do not have to poll for
progress (see [Step 5](#step-5-monitor-status)):

```bash theme={null}
curl -X POST "$BASE_URL/packages" \
  -H "Authorization: Bearer {access_token}" \
  -H "Content-Type: application/json" \
  -H "User-Agent: MyApp/1.0" \
  -d '{
    "Name": "Employment Contract",
    "Initiator": "sender@yourcompany.com",
    "CallBackUrl": "https://yourapp.example.com/nsev/callback"
  }'
```

See [Callbacks and redirects](/docs/nsev/build/guides/callbacks) for the callback
payload, retry behavior, and the related notification and redirect options.

***

## Step 2: Upload a document

Documents are sent as base64-encoded content in a JSON request body. Property names are
PascalCase, and `DocumentOptions` (with a `ContentType` and `Base64data`) is required.

**Encode your PDF:**

```bash theme={null}
# Linux
export DOC_BASE64=$(base64 -w 0 contract.pdf)

# macOS
export DOC_BASE64=$(base64 -i contract.pdf | tr -d '\n')
```

**Upload the document:**

```bash theme={null}
curl -X POST "$BASE_URL/packages/$PACKAGE_ID/documents" \
  -H "Authorization: Bearer {access_token}" \
  -H "Content-Type: application/json" \
  -H "User-Agent: MyApp/1.0" \
  -d "{
    \"Name\": \"Employment Contract\",
    \"Language\": \"en\",
    \"DocumentOptions\": {
      \"ContentType\": \"application/pdf\",
      \"TargetType\": \"application/pdf\",
      \"PdfOptions\": {
        \"TargetFormat\": \"pdf\",
        \"PdfErrorHandling\": \"DetectFixWarn\"
      },
      \"Base64data\": \"$DOC_BASE64\"
    }
  }"
```

Save the document `Id` from the response:

```json theme={null}
{
  "Id": "1e501b99-9f0f-4f12-9ad9-6c737c6394e8",
  "PackageId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "Name": "Employment Contract",
  "Status": "Draft"
}
```

```bash theme={null}
export DOCUMENT_ID="1e501b99-9f0f-4f12-9ad9-6c737c6394e8"
```

<Note>
  A document must not exceed 30 MB. A package must not exceed 150 MB in total and should not contain
  more than 15 documents.
</Note>

<Tip>
  To upload and process larger documents without blocking, pass `?async=true`. The response is a
  `202 Accepted` with a `Location` header you can poll for processing status.
</Tip>

***

## Step 3: Set the process

The **process** defines who does what, and in which order. It is a list of steps that run
sequentially; each step is a set of actors (a stakeholder plus the action they perform) that run
in parallel within that step.

Set the whole process in one call. Here it has two steps:

1. Jane **signs** - the `Signer` actor carries a signing element that specifies where on the
   document the signature is placed
2. John **receives** the fully signed document - the `Receiver` actor takes no action, and the
   receiver step must always come last

```bash theme={null}
curl -X PUT "$BASE_URL/packages/$PACKAGE_ID/process" \
  -H "Authorization: Bearer {access_token}" \
  -H "Content-Type: application/json" \
  -H "User-Agent: MyApp/1.0" \
  -d "[
    [
      {
        \"Type\": \"Signer\",
        \"StakeholderId\": \"$SIGNER_STAKEHOLDER_ID\",
        \"SuppressNotifications\": false,
        \"Elements\": [
          {
            \"Type\": \"SigningField\",
            \"DocumentId\": \"$DOCUMENT_ID\",
            \"SigningMethods\": [\"manual:manual\"],
            \"Location\": {
              \"Page\": 1,
              \"Top\": 600,
              \"Left\": 100
            },
            \"Dimensions\": {
              \"Width\": 200,
              \"Height\": 70
            }
          }
        ]
      }
    ],
    [
      {
        \"Type\": \"Receiver\",
        \"StakeholderId\": \"$RECEIVER_STAKEHOLDER_ID\"
      }
    ]
  ]"
```

A `200 OK` response confirms the process was set. The call **overwrites** the package's entire
process, so it is safe to repeat while you iterate on a draft; to add steps incrementally instead,
use `POST /packages/{packageId}/process`.

The signing element coordinates are in points (1 point = 1/72 inch) from the top-left of the page.
Adjust `Page`, `Top`, and `Left` to position the signature field where you want it on your document.
A signing field must be at least 50 × 30 points.

<Note>
  A step may not mix actor types (except FormFillers with Approvers), approval and form-filling
  steps must precede all signing steps, and the receiver step is always parallel and always last.
</Note>

<Tip>
  `SigningMethods` references the methods configured for your tenant (for example, `manual:manual` or
  `smartcard:beid`). The names are case-sensitive. Call `GET /signingmethods` to list the methods
  available to you and use the returned `Name` values here.
</Tip>

***

## Step 4: Activate the package

Once the package is fully configured, set its status to `pending` to start the signing workflow.
The signer will receive an email notification with a link to sign; once all signing is complete,
the receiver is notified with a link to download the signed document.

```bash theme={null}
curl -X PUT "$BASE_URL/packages/$PACKAGE_ID/status" \
  -H "Authorization: Bearer {access_token}" \
  -H "Content-Type: application/json" \
  -H "User-Agent: MyApp/1.0" \
  -d '"pending"'
```

A `204 No Content` response confirms the package was activated.

<Tip>
  Before activating, you can call `GET /packages/{packageId}/warnings` to check for configuration
  issues such as missing signing elements or incomplete stakeholder data.
</Tip>

***

## Step 5: Monitor status

You have two ways to learn when the package progresses, and you can use either:

* **Callbacks (recommended for integrations).** If you set a `CallBackUrl` in
  [Step 1](#step-1-create-the-package-with-its-stakeholders), NSEV `POST`s your system on each
  status change, so you do not have to poll. This reacts immediately and scales better. See
  [Callbacks and redirects](/docs/nsev/build/guides/callbacks).
* **Polling (fine for simple flows).** Call `GET /packages/{packageId}/status` periodically until
  the package reaches a terminal status.

To poll the status:

```bash theme={null}
curl "$BASE_URL/packages/$PACKAGE_ID/status" \
  -H "Authorization: Bearer {access_token}" \
  -H "User-Agent: MyApp/1.0"
```

These are the statuses you will most commonly see:

| Status     | Meaning                                                  |
| ---------- | -------------------------------------------------------- |
| `Draft`    | Package is being configured                              |
| `Pending`  | Package is activated, waiting for the first signer       |
| `Finished` | Signing complete - documents are ready to download       |
| `Rejected` | A signer rejected the package                            |
| `Revoked`  | Package was cancelled                                    |
| `Expired`  | Package exceeded its expiry date without being completed |
| `Failed`   | Processing failed - the package cannot be completed      |

Once the status is `Finished`, download the signed documents with `GET /packages/{packageId}/download`.

***

## Next steps

* Try the faster variant: [Instant package](/docs/nsev/build/tutorials/instant-package) creates the package, document, signer, and signing field in a single call
* Wire up [Callbacks and redirects](/docs/nsev/build/guides/callbacks) to replace polling and control where signers land after acting
* Understand the objects you assembled here in [The package model](/docs/nsev/build/concepts/package-model)
* Browse the full set of operations in the [API Reference](/docs/nsev/getting-started/first-call)
* Review [Best Practices](/docs/nsev/build/guides/good-practices) for error handling, pagination, and security
