# Create documents

URL: https://docs.hanzo.ai/docs/openapi/esign/post-esign-documents

> Uploads a PDF and opens a draft ready for recipients and fields.

`POST /v1/esign/documents`

> [eSign →](https://docs.hanzo.ai/docs/openapi/esign) · [All API references →](https://docs.hanzo.ai/docs/openapi) · [Get started →](https://docs.hanzo.ai/docs/guides)

|               |                                           |
| ------------- | ----------------------------------------- |
| **Address**   | `https://api.hanzo.ai/v1/esign/documents` |
| **Method**    | `POST`                                    |
| **Operation** | `post_esign_documents`                    |
| **Auth**      | `Authorization: Bearer $HANZO_API_KEY`    |

Uploads a PDF and opens a draft ready for recipients and fields.

It answers 201 with the document in DRAFT — the state where recipients and
fields may still be added, and the only state they may. The bytes go to object
storage rather than into the tenant database, and the original is kept under its
own key so it survives sealing untouched: a completed document can always be
compared against what was uploaded. Creation is recorded on the audit trail.

This is the sender's surface: a validated principal is required, and the document
lands in that principal's OWN org. Isolation is physical rather than a filter —
each tenant has its own store — so another org's document id is simply not
there. A body over 32 MiB is refused with 413.

## Request

6 fields, body `application/json` (required).

| Field          | In   | Type | Required | Description                                                                                                                                       |
| -------------- | ---- | ---- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------- |
| `externalId`   | body | any  | —        | ExternalID is your own identifier for this document, stored and echoed back so a document here can be matched to a record in your system.         |
| `message`      | body | any  | —        | Message is the covering message carried with the document.                                                                                        |
| `pdfBase64`    | body | any  | —        | PdfBase64 is the document itself, base64-encoded; a `data:` URL prefix is accepted and stripped.                                                  |
| `signingOrder` | body | any  | —        | SigningOrder chooses PARALLEL — the default, where everyone may sign at once — or SEQUENTIAL, where each signer waits for the ones ahead of them. |
| `subject`      | body | any  | —        | Subject is the covering subject line carried with the document.                                                                                   |
| `title`        | body | any  | —        | Title is the document's name, shown to every recipient and used to build the download filename.                                                   |

## Response

| Status    | Body                | Meaning |
| --------- | ------------------- | ------- |
| `201`     | esign.esignDocument | created |
| `default` | problem-details     | refused |

`201` body — 34 fields.

| Field                          | In   | Type                    | Always | Description                                                                                                                                                                          |
| ------------------------------ | ---- | ----------------------- | ------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `completedAt`                  | body | integer (int64)         | —      | CompletedAt is when the document sealed, in unix milliseconds; null until it does.                                                                                                   |
| `createdAt`                    | body | integer (int64)         | —      | CreatedAt is when the document was uploaded, in unix milliseconds.                                                                                                                   |
| `externalId`                   | body | string                  | —      | ExternalID is the caller's own identifier for this document, echoed back as it was given; null when none was.                                                                        |
| `fields`                       | body | esign.esignField\[]     | —      | Fields is every field on the document, ordered by page and then by when it was placed.                                                                                               |
| `fields[].customText`          | body | string                  | —      | CustomText is the value a non-signature field was filled with, empty until it is.                                                                                                    |
| `fields[].fieldMeta`           | body | any                     | —      | FieldMeta is the caller's own metadata for this field, stored verbatim at placement and never interpreted.                                                                           |
| `fields[].height`              | body | number (double)         | —      | Height is the field's height, -1 when the renderer is to choose one.                                                                                                                 |
| `fields[].id`                  | body | string                  | —      | ID is the field id.                                                                                                                                                                  |
| `fields[].inserted`            | body | boolean                 | —      | Inserted is whether this field has been filled in.                                                                                                                                   |
| `fields[].page`                | body | number (double)         | —      | Page is the 1-based page the field sits on.                                                                                                                                          |
| `fields[].positionX`           | body | number (double)         | —      | PositionX is the field's horizontal position on that page.                                                                                                                           |
| `fields[].positionY`           | body | number (double)         | —      | PositionY is the field's vertical position on that page.                                                                                                                             |
| `fields[].recipientId`         | body | string                  | —      | RecipientID is who must fill this field.                                                                                                                                             |
| `fields[].type`                | body | string                  | —      | Type is what the field collects — SIGNATURE, DATE, NAME, EMAIL, TEXT and the rest.                                                                                                   |
| `fields[].width`               | body | number (double)         | —      | Width is the field's width, -1 when the renderer is to choose one.                                                                                                                   |
| `id`                           | body | string                  | —      | ID is the document id.                                                                                                                                                               |
| `message`                      | body | string                  | —      | Message is the covering message stored with the document; null when none was given.                                                                                                  |
| `recipients`                   | body | esign.esignRecipient\[] | —      | Recipients is everyone on the document, ordered by signing order and then by when they were added — which is also the order a SEQUENTIAL document enforces.                          |
| `recipients[].email`           | body | string                  | —      | Email is where this recipient's signing link is meant to go, lower-cased.                                                                                                            |
| `recipients[].id`              | body | string                  | —      | ID is the recipient id, which is what a field is placed against.                                                                                                                     |
| `recipients[].name`            | body | string                  | —      | Name is the recipient's display name, empty when none was given.                                                                                                                     |
| `recipients[].readStatus`      | body | string                  | —      | ReadStatus is NOT\_OPENED until they first open their link, then OPENED.                                                                                                             |
| `recipients[].rejectionReason` | body | string                  | —      | RejectionReason is why they declined, null unless they did.                                                                                                                          |
| `recipients[].role`            | body | string                  | —      | Role is SIGNER, CC, VIEWER, APPROVER or ASSISTANT.                                                                                                                                   |
| `recipients[].sendStatus`      | body | string                  | —      | SendStatus is NOT\_SENT until the document goes out, then SENT.                                                                                                                      |
| `recipients[].signedAt`        | body | integer (int64)         | —      | SignedAt is when they finished or declined, in unix milliseconds; null while neither has happened.                                                                                   |
| `recipients[].signingOrder`    | body | number (double)         | —      | SigningOrder is their position in a SEQUENTIAL document, null when they were added without one.                                                                                      |
| `recipients[].signingStatus`   | body | string                  | —      | SigningStatus is NOT\_SIGNED, SIGNED or REJECTED.                                                                                                                                    |
| `signingOrder`                 | body | string                  | —      | SigningOrder is PARALLEL or SEQUENTIAL, fixed when the document was created.                                                                                                         |
| `source`                       | body | string                  | —      | Source is how the document came to exist.                                                                                                                                            |
| `status`                       | body | string                  | —      | Status is DRAFT while recipients and fields may still be added, PENDING once it has gone out, then COMPLETED when every signer has finished or REJECTED if any one of them declined. |
| `subject`                      | body | string                  | —      | Subject is the covering subject line stored with the document; null when none was given.                                                                                             |
| `title`                        | body | string                  | —      | Title is the document's name, and the stem of the download filename.                                                                                                                 |
| `updatedAt`                    | body | integer (int64)         | —      | UpdatedAt is when the document last changed, in unix milliseconds.                                                                                                                   |

Failure carries the platform error shape — see [Errors](https://docs.hanzo.ai/docs/errors).

## Examples

**CLI**

```bash
hanzo esign documents create
```

**SDK**

**.ts**

```ts
import { Configuration, EsignApi } from 'hanzoai';

const api = new EsignApi(new Configuration({ accessToken: process.env.HANZO_API_KEY }));
const { data } = await api.postEsignDocuments({ externalId: "<externalId>", message: "<message>" });
```

**.py**

```python
from hanzoai.cloud import ApiClient, Configuration
from hanzoai.cloud.api import EsignApi

client = ApiClient(Configuration(access_token=os.environ["HANZO_API_KEY"]))
result = EsignApi(client).post_esign_documents(external_id="<externalId>", message="<message>")
```

**.go**

```go
cfg := hanzoai.NewConfiguration()
cfg.AddDefaultHeader("Authorization", "Bearer "+os.Getenv("HANZO_API_KEY"))
client := hanzoai.NewAPIClient(cfg)

resp, _, err := client.EsignAPI.PostEsignDocuments(context.Background()).Execute()
if err != nil {
	return err
}
```

**.rs**

```rust
use hanzo_client::apis::{configuration::Configuration, esign_api};

let mut cfg = Configuration::new();
cfg.bearer_access_token = std::env::var("HANZO_API_KEY").ok();

let result = esign_api::post_esign_documents(&cfg, Default::default()).await?;
```

**.java**

```java
import ai.hanzo.cloud.ApiClient;
import ai.hanzo.cloud.api.EsignApi;

ApiClient client = new ApiClient();
client.setBearerToken(System.getenv("HANZO_API_KEY"));

var result = new EsignApi(client).postEsignDocuments();
```

**HTTP**

```bash
curl -X POST https://api.hanzo.ai/v1/esign/documents \
  -H "Authorization: Bearer $HANZO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
       "externalId": "<externalId>",
       "message": "<message>"
     }'
```

**MCP**

MCP reaches **esign** through the `esign` tool, which names its 9 operations with its own verbs — this one among them, under a name only MCP declares. `describe` explains any of them:

```bash
curl -X POST https://api.hanzo.ai/v1/mcp \
  -H "Content-Type: application/json" \
  -d '{
       "jsonrpc": "2.0",
       "id": 1,
       "method": "tools/call",
       "params": {
         "name": "describe",
         "arguments": {
           "op": "list_esign_documents"
         }
       }
     }'
```

---

[eSign API](https://docs.hanzo.ai/docs/openapi/esign) · [All Hanzo APIs](https://docs.hanzo.ai/docs/openapi) · [Interactive reference](https://docs.hanzo.ai/docs/openapi/reference)
