---
title: Signing
description: Send an organization's contracts out for electronic signature through your signing provider, and report each outcome back.
---

The signing API is for an e-signing connector: an app that connects an organization to an electronic signing provider. An operator sends an agreement for signature in Hydda One. Hydda One renders the contract as a PDF and queues it. Your connector creates the document at the provider, invites the parties, and reports what happened.

Your connector does all the calling. Hydda One never calls it. The connector reads work from two queues and reports each outcome on its own route.

## What the token needs

The caller is an installed connector, with a token from the installation-token grant. See [Authentication](/guides/authentication).

The installation must hold the `Signing Integration` role at each business it signs for. Hydda One grants the role when an administrator saves the connector's configuration at that business. A business has one signing connector at a time. See [Roles](/guides/roles).

Operators can send contracts for signing at a business only after your configuration there is `ready`. See [Integrations](/guides/surfaces/integrations) for how to report it.

A token without an organization answers `403`. So does a token whose installation holds the role nowhere.

## The flow

```mermaid
sequenceDiagram
  participant C as Your connector
  participant H as Hydda One
  participant P as Signing provider
  C->>H: GET /v1/crm/signing-queue
  H-->>C: queued signing attempts
  C->>H: GET documentAddress
  H-->>C: the contract PDF
  C->>P: create the document and invite the parties
  C->>H: POST /v1/crm/signing-attempts/{attemptId}/started
  P-->>C: every party signed
  C->>H: POST /v1/crm/signing-attempts/{attemptId}/signed-document-upload
  H-->>C: uploadUrl and objectKey
  C->>H: PUT uploadUrl with the signed PDF
  C->>H: POST /v1/crm/signing-attempts/{attemptId}/signed
```

Each call is described in the [signing reference](/reference/signing).

1. **Poll the signing queue.** Call `GET /v1/crm/signing-queue`. Each item is one signing attempt, oldest first.
2. **Download the contract.** Fetch the PDF from the item's `documentAddress`. The address expires after 15 minutes, and every poll returns a new one. The PDF is the whole contract. Send it for signing as it is.
3. **Create the document at the provider.** Use the credentials of your configuration at the item's `businessContextId`. Use `title` as the document's title and `locale` as its language. Invite the `parties`.
4. **Report `started`.** Call `POST /v1/crm/signing-attempts/{attemptId}/started` with `providerReference`, your id for the document at the provider. If you could not create the document, report `failed` instead.
5. **Report the outcome.** When the provider tells you how it ended, report `signed`, `declined` or `expired`.
6. **Handle withdrawals.** Poll `GET /v1/crm/withdrawal-queue`. Cancel each listed document at the provider, then report `withdrawn`.

### The parties

`parties` lists the people who sign, in the order the operator named them. At least one party signs for the operator, and one for the customer. Each party has a `side`, `operator` or `customer`, and an `email`. It can also carry a `name`, a `phone` and a `companyName`.

When `ordered` is `true`, the parties sign one after another, in list order. When it is `false`, invite them all at once.

The parties are personal data. Hydda One sends them only so that you can invite the parties at the provider.

### Reporting a signed document

A signed report brings the signed PDF with it. It takes three calls.

1. Call `POST /v1/crm/signing-attempts/{attemptId}/signed-document-upload` with `contentType` and `sizeBytes`. `contentType` must be `application/pdf`. `sizeBytes` is the exact size of the PDF, up to 25 MB.
2. Upload the PDF with an HTTP `PUT` to `uploadUrl`. Send `Content-Type: application/pdf` and exactly `sizeBytes` bytes. The address works for 300 seconds.
3. Call `POST /v1/crm/signing-attempts/{attemptId}/signed` with the `objectKey` from step 1.

Hydda One then stores the signed PDF and publishes the agreement.

A new upload request replaces the previous one. The earlier upload is deleted, so report the newest `objectKey`.

The upload request answers `204` with no body when the attempt is not `started`. There is then nothing to upload.

### Withdrawals

An operator can withdraw a contract while it is out for signature. A `started` attempt then appears on the withdrawal queue. The item carries the `providerReference` you reported.

Cancel the document at the provider, then report `withdrawn`. If the provider says every party signed first, report `signed` instead. Hydda One accepts the signature, because the provider decides which came first.

An operator can also withdraw an attempt that is still `queued`. It ends at once and never reaches the withdrawal queue. If you had already read it from the queue, your `started` report answers `409` with `CRM_SIGNING_ATTEMPT_ENDED`. Cancel the document you created at the provider. No attempt follows it.

## Attempt statuses

| `status` | Meaning |
| --- | --- |
| `queued` | The operator sent the contract. Your connector has not reported on it yet. |
| `started` | You created the document at the provider and reported `started`. |
| `signed` | Every party signed. The agreement is published. |
| `declined` | A party declined to sign. |
| `expired` | The document expired at the provider. |
| `withdrawn` | The operator withdrew it. |
| `failed` | You could not create the document. |

Each report moves the attempt from certain statuses only:

| Report | Accepted from |
| --- | --- |
| `started` | `queued` |
| `signed`, `declined`, `expired` | `started` |
| `withdrawn`, `failed` | `queued`, `started` |

Every end except `signed` returns the agreement to a draft. The operator can fix it and send it again. A new send creates a new attempt with a new `attemptId`.

## Rules for callers

### A queue is not consumed by reading it

Reading a queue claims nothing. An item stays on its queue until a report changes its status. If your connector crashes, poll again and you get the same items.

Work can therefore arrive twice. Your connector may create a document and fail before it reports. It then gets the item again. Keep track of the `attemptId` values you already created documents for.

### A repeated report changes nothing

A repeated or late report answers `204` and writes nothing. This covers a report the attempt's status no longer accepts. Retry a report safely after a timeout.

`started` for an attempt that already ended is the one exception. It answers `409` with `CRM_SIGNING_ATTEMPT_ENDED`. A repeated `started` for a `started` attempt still answers `204`.

### Limits

| What | Limit |
| --- | --- |
| `limit` on both queues | 1 to 200. Defaults to 50. |
| `providerReference` | Up to 200 characters. |
| `reason` on declined and failed | 1 to 500 characters. Optional on declined. |
| `code` on failed | Lower snake case, such as `document_rejected`. Up to 100 characters. |
| Signed PDF | Up to 25 MB. |
| Requests under `/v1/crm` | 300 a minute from each IP address. |

A request over the rate limit gets `429`.

## Errors to handle

| Code | Status | When |
| --- | --- | --- |
| `CRM_SIGNING_ATTEMPT_ENDED` | 409 | You reported `started` for an attempt that already ended. Cancel the document at the provider. |
| `CRM_SIGNED_DOCUMENT_REJECTED` | 400 | The `objectKey` is not the attempt's latest upload, or the upload is missing, over 25 MB or not a PDF. Request a new upload and try again. |
| `VALIDATION_FAILED` | 400 | A body, `limit` or path parameter does not match its schema. |

A `404` without a `code` means the attempt does not exist, or your installation cannot reach it.

[Errors](/guides/errors) lists every code and its status.
