---
title: "Set a document's contract terms"
method: PUT
path: "/v1/accounts/{accountId}/documents/{documentId}/contract"
tags: ["Contracts"]
---

# Set a document's contract terms

`PUT /v1/accounts/{accountId}/documents/{documentId}/contract`

## Request

- Base URL: `https://api.assinafy.com.br`
- URL: `https://api.assinafy.com.br/v1/accounts/{accountId}/documents/{documentId}/contract`
- Auth: one of:
  - HTTP bearer
  - API key in header `X-Api-Key`

Creates the document's contract terms, or changes only the attributes sent (see the request body for what `null` does). `external_references`, when sent, replaces the whole list. Records a `document_contract_updated` event when the terms change; a retry that changes nothing records none. The body must be a JSON object with at least one attribute. With `parent_document_id`, the document is instead linked as an amendment, renewal or termination of that contract (or of any document in its family, resolved to the root) and the other attributes are the terms it changes; a later `PUT` on a pending amendment merges into that delta (a `null` drops a key) and `parent_document_id: null` unlinks it; every change to the link records `document_contract_amendment_updated` on this document. The delta applies to the parent once the document is certificated (at once, if it already is), in signing order (the order the documents' certifications were recorded in, fixed for each once it is; a certificated document with none recorded waits, and is placed by its certification time an hour on): instruments signed earlier apply first, one that cannot holds the later ones back, and an instrument signed before one already applied is blocked instead of applied over it. Applying records `document_contract_amended`; an applied amendment is read-only. A cancelled, declined or expired document takes no new terms and no new link; terms or a link already on one stay and can still change. Send the `updated_at` you read with the write to have it refused (`409`) if the contract or link changed meanwhile. Needs a plan with contract tracking (`403` otherwise).

[Copy as Markdown](https://api.assinafy.com.br/v1/docs/markdown?method=put&path=%2Fv1%2Faccounts%2F%7BaccountId%7D%2Fdocuments%2F%7BdocumentId%7D%2Fcontract) [Open in ChatGPT](https://chatgpt.com/?q=Read%20the%20API%20documentation%20at%20https%3A%2F%2Fapi.assinafy.com.br%2Fv1%2Fdocs%2Fmarkdown%3Fmethod%3Dput%26path%3D%252Fv1%252Faccounts%252F%257BaccountId%257D%252Fdocuments%252F%257BdocumentId%257D%252Fcontract%20and%20help%20me%20integrate%20this%20endpoint.) [Open in Claude](https://claude.ai/new?q=Read%20the%20API%20documentation%20at%20https%3A%2F%2Fapi.assinafy.com.br%2Fv1%2Fdocs%2Fmarkdown%3Fmethod%3Dput%26path%3D%252Fv1%252Faccounts%252F%257BaccountId%257D%252Fdocuments%252F%257BdocumentId%257D%252Fcontract%20and%20help%20me%20integrate%20this%20endpoint.)

## Path parameters

- `accountId` string, required
- `documentId` string, required

## Request body

- DocumentContractInput: Every attribute is optional and only the ones sent change. `null` clears an attribute, except `auto_renew` (back to `false`) and `currency` (back to `BRL` while a `value` is set); `status` does not accept `null`. A changed `end_date` starts a new `term`.
  - `status` 'draft' | 'active' | 'expired' | 'terminated': On creation any status, so a contract that ended before it was tracked is registered as it ended: `expired` needs an `end_date` in the past, `terminated` its `end_date`; neither is walked by the lifecycle. Afterwards only `draft` or `active` (a contract in force never goes back to `draft`): `expired` and `terminated` are set by the lifecycle and the terminate endpoint; a closed contract is reopened by renewing it.
  - `counterparty_name` string, nullable: Emoji are kept; other characters outside the 3-byte range are dropped.
  - `counterparty_taxpayer_id` string, nullable: A valid CPF or CNPJ, formatted or not.
  - `value` number, nullable: Two decimal places at most.
  - `currency` string, nullable: Three letters, such as `BRL` or `USD`, stored in capitals; `BRL` when a `value` is set without one.
  - `start_date` string, date, nullable: `YYYY-MM-DD`, from the year 1000.
  - `end_date` string, date, nullable: `YYYY-MM-DD`, on or after `start_date`.
  - `auto_renew` boolean: Whether the hourly run starts the next term the day after `end_date`, by `renewal_term_months`, instead of expiring the contract.
  - `renewal_term_months` integer, nullable: How far a renewal by the term moves `end_date`; 12 when unset.
  - `notice_period_days` integer, nullable: Days before `end_date` the notice period starts (`document_contract_renewal_notice`); none when unset.
  - `owner_id` string, nullable: An active member of this workspace.
  - `metadata` object, nullable: Your own fields: up to 50 keys of 1 to 40 letters, digits, `_`, `-` or `.`, each holding plain text of at most 500 characters; no number under a key ending in `_at`, or `stuck_since`.
  - `external_references` object[], nullable: Replaces the whole list; `[]` or `null` clears it. Two items whose `system` and `id` compare equal case aside are one reference (the first sent is kept); a pair the database takes for one beyond that (accents, `ß` and `ss`) answers `400`.
    - `system` string, required: Letters, digits, `_`, `-` and `.`; stored in lower case.
    - `id` string, required: Kept as sent; must not contain emoji or other 4-byte characters.
  - `parent_document_id` string, nullable: Links the document as an amendment of this contract document, or of any document in its family (resolved to the root); `null` unlinks a pending amendment. With it, the other attributes are the terms the instrument changes, not terms of its own; on a pending amendment a `null` value drops that term from the delta rather than clearing it on the parent.
  - `relation_type` 'amendment' | 'renewal' | 'termination': Required on the first link. `amendment` may change `value`, `currency`, `start_date` (effective from, display only), `end_date`, `auto_renew`, `renewal_term_months`, `notice_period_days`, `counterparty_name`, `counterparty_taxpayer_id` and `metadata`; `renewal` only `end_date` and `value`; `termination` only `end_date` (the effective date).
  - `updated_at` string, date-time: Optional precondition: the `updated_at` the client read the contract (or this document's amendment link) with, as the API returned it. If the contract or link changed since, nothing is written and the answer is `409` with `code: ContractChanged`. Not sent, or `null`: the write applies whatever changed meanwhile (last write wins). Compared to the second; a fraction of a second, as a JavaScript date prints it, is read and ignored. New terms and a first link have nothing to have read, so sending one answers `409`; on a pending amendment, send the link's own `updated_at` (the GET on that document).

## Response `200`

The contract terms as saved, or the amendment as linked

- object: Standard success wrapper. Operations add their own `data`.
  - `status` integer: HTTP status code, mirrored in the body.
  - `message` string: Human-readable message; empty on success.
  - `data` union
    - DocumentContract: The contract terms tracked on a document.
      - `resource` string
      - `document_id` string
      - `status` 'draft' | 'active' | 'expired' | 'terminated'
      - `counterparty_name` string, nullable
      - `counterparty_taxpayer_id` string, nullable: CPF or CNPJ, digits only.
      - `value` number, nullable: Two decimal places at most.
      - `currency` string, nullable: ISO 4217 code; `BRL` when a value is sent without one.
      - `start_date` string, date, nullable
      - `end_date` string, date, nullable
      - `auto_renew` boolean
      - `renewal_term_months` integer, nullable
      - `notice_period_days` integer, nullable
      - `term` integer: Steps on every renewal, starting at 1.
      - `owner_id` string, nullable: ID of the workspace member who owns the contract. When the owner is no longer a member of the workspace (removed, left, or deleted with their account), the hourly run hands the contract to the first owner who is still an active member, or leaves it without one; an inactive member stays the owner. The contract's e-mails (renewal notice, expiring, expired, renewed) go to the first of the owner and the document's creator who is an active member, as that member's notification preferences allow.
      - `metadata` object, nullable: Up to 50 keys of your own, each holding text of at most 500 characters; `null` when there are none. A key ending in `_at` cannot hold a number: the API returns numbers under such keys as dates.
      - `external_references` object[]: IDs outside systems use for this contract, ordered by system and id.
        - `system` string
        - `id` string
      - `document` Document: A document and its current lifecycle state.
        - `resource` string: Present in single-resource responses.
        - `id` string
        - `account_id` string
        - `template_id` string, nullable
        - `name` string
        - `status` string: Status code — see GET /v1/documents/statuses.
        - `artifacts` object: Artifact download URLs keyed by name. Always `original`, plus `thumbnail` once one exists. A certificated document also carries `certificated`, `certificate-page` and `bundle`, and `pades` when it was signed with a digital certificate — the PAdES version holds the signers' ICP-Brasil signatures, which certification flattens out of the certificated PDF.
        - `is_closed` boolean
        - `signing_url` string
        - `verification_code` string, nullable: The code printed on the certificate page. Anyone can check it with `GET /v1/documents/{verification_code}/verify` or on `verification_url`. `null` until the document is `certificated`, and on documents certificated before verification codes existed.
        - `verification_url` string, uri, nullable: The public verification page for this document. `null` until the document is `certificated`.
        - `decline_reason` string, nullable
        - `declined_by` Signer: A signing party belonging to a workspace account.
          - `resource` string: Present in single-resource responses.
          - `id` string
          - `full_name` string
          - `email` string, email, nullable
          - `whatsapp_phone_number` string, nullable: E.164 format; normalized on save.
          - `government_id` string, nullable: Signer's normalized CPF (11 digits) or CNPJ (14 characters; may be alphanumeric).
          - `has_accepted_terms` boolean
        - `tags` object[]
          - `id` string
          - `name` string
        - `assignment` Assignment: A request for signers to sign a document.
          - `resource` string
          - `id` string
          - `document_id` string, nullable: The document this assignment belongs to. `null` only if the document was deleted.
          - `created_at` string, date-time
          - `sender_email` string, email, nullable: Email of the user who sent the document; null once that user has deleted their account.
          - `method` 'virtual' | 'collect'
          - `expires_at` string, date-time, nullable
          - `message` string, nullable
          - `signers` AssignmentSigner[]
            - `resource` string: Present in single-resource responses.
            - `id` string
            - `full_name` string
            - `email` string, email, nullable
            - `whatsapp_phone_number` string, nullable: E.164 format; normalized on save.
            - `government_id` string, nullable: Signer's normalized CPF (11 digits) or CNPJ (14 characters; may be alphanumeric).
            - `has_accepted_terms` boolean
            - `verification_method` string, nullable
            - `notification_methods` string[], nullable
            - `step` integer, nullable: Sequential signing step (defaults to 1).
            - `notified` boolean, nullable
            - `identity_verification_status` 'Pending' | 'InReview' | 'Approved' | 'Declined' | 'Expired' | 'Abandoned', nullable: Latest identity-document verification status; `InReview` means the account must decide it before the document can be certificated.
            - `completed` boolean, nullable: Only present in account-owner contexts.
            - `notification_history` NotificationHistoryEntry[], nullable: Per-channel delivery history for this signer (email + WhatsApp), most-recent send order.
              - …
          - `copy_receivers` object[]
          - `items` AssignmentItem[]
            - `id` string
            - `page` DocumentPage
              - …
            - `signer` object: Signer responsible for this item.
            - `field` object, nullable: Field definition associated with the item.
            - `display_settings` unknown
            - `value` unknown
            - `completed` boolean
          - `summary` AssignmentSummary
            - `signer_count` integer
            - `completed_count` integer
            - `signers` object[]
              - …
          - `signing_urls` SigningUrl[]
            - `signer_id` string
            - `url` string
        - `pages` DocumentPage[]: The document's pages. Included on the same responses as `assignment`, and also on the upload response.
          - `id` string
          - `number` integer
          - `height` integer
          - `width` integer
          - `download_url` string
        - `has_contract` boolean: Only on `GET /v1/documents/{documentId}` read by the workspace, while the environment has contract tracking on: not with a signer's access code, not in document lists or webhooks, and left out when the lookup fails, so a missing key means unknown. Whether `GET /v1/accounts/{accountId}/documents/{documentId}/contract` answers with contract terms, the document's own or those of a contract it amends whose document still exists, so a page can lay out the contract section before reading it.
        - `created_at` string, date-time
        - `updated_at` string, date-time
      - `amendments` DocumentContractAmendment[]: Only on the single-contract read: the documents that amend, renew or terminate this contract, the applied ones first by `number`, then the pending ones.
        - `resource` string
        - `document_id` string: This document, the instrument.
        - `document_name` string: This document's name.
        - `parent_document_id` string: The contract document whose terms change.
        - `relation_type` 'amendment' | 'renewal' | 'termination'
        - `number` integer, nullable: Position among the parent's applied amendments by signing date; `null` while pending.
        - `status` 'pending' | 'applied': `applied` once the delta moved the parent's terms.
        - `document_status` string: This document's own status, so a declined, cancelled or expired instrument shows as such. An instrument applies only while its document is `certificated`: one whose document reads `failed` after it was signed waits, with no `apply_error`, until it is certificated again, and then applies in its place in the signing order.
        - `apply_error` string, nullable: Why a signed instrument has not applied yet, retried every hour: the workspace's plan does not include contract tracking (it applies on the first run after the plan returns), the parent would not take the delta, an instrument signed before this one has not applied yet, one signed after it has already applied (this one then stays blocked: unlink it, and record what it still changes on a new instrument), or an unexpected failure.
        - `currency` string, nullable: The currency of the amounts in `terms` and `changes`. Once applied, the contract's currency at that moment, kept whatever the contract moves to later. While pending (and for an instrument applied before this was kept), the one this instrument moves the contract to, otherwise the contract's. `null` when neither has one.
        - `terms` object, nullable: The delta: only the terms this instrument changes; `null` when it changes none (a termination on the day it is signed).
        - `changes` object, nullable: Every parent field the roll-up moves, as `{from, to}`: a preview while pending, the record once applied; `null` when it moves none.
        - `applied_at` string, date-time, nullable
        - `created_at` string, date-time
        - `updated_at` string, date-time
      - `created_at` string, date-time
      - `updated_at` string, date-time
    - DocumentContractAmendment: A document that amends, renews or terminates a contract: its link and the terms it changes. The parent's terms take the delta once this document is certificated; until then the parent is untouched.
      - `resource` string
      - `document_id` string: This document, the instrument.
      - `document_name` string: This document's name.
      - `parent_document_id` string: The contract document whose terms change.
      - `relation_type` 'amendment' | 'renewal' | 'termination'
      - `number` integer, nullable: Position among the parent's applied amendments by signing date; `null` while pending.
      - `status` 'pending' | 'applied': `applied` once the delta moved the parent's terms.
      - `document_status` string: This document's own status, so a declined, cancelled or expired instrument shows as such. An instrument applies only while its document is `certificated`: one whose document reads `failed` after it was signed waits, with no `apply_error`, until it is certificated again, and then applies in its place in the signing order.
      - `apply_error` string, nullable: Why a signed instrument has not applied yet, retried every hour: the workspace's plan does not include contract tracking (it applies on the first run after the plan returns), the parent would not take the delta, an instrument signed before this one has not applied yet, one signed after it has already applied (this one then stays blocked: unlink it, and record what it still changes on a new instrument), or an unexpected failure.
      - `currency` string, nullable: The currency of the amounts in `terms` and `changes`. Once applied, the contract's currency at that moment, kept whatever the contract moves to later. While pending (and for an instrument applied before this was kept), the one this instrument moves the contract to, otherwise the contract's. `null` when neither has one.
      - `terms` object, nullable: The delta: only the terms this instrument changes; `null` when it changes none (a termination on the day it is signed).
      - `changes` object, nullable: Every parent field the roll-up moves, as `{from, to}`: a preview while pending, the record once applied; `null` when it moves none.
      - `applied_at` string, date-time, nullable
      - `created_at` string, date-time
      - `updated_at` string, date-time

## Other responses

- `400`: The request breaks a contract rule. `errors` has one entry per problem found: the `field` as sent (`null` when the request as a whole is refused), a stable `code` to translate, and the `message` in the platform's language; `message` at the top is the first entry's. The contract's own rules on the terms, and the external references' items, report every field that breaks one, an entry per field (its first problem); the other checks (the body, its fields, the contract's state, a renewal's or a link's conditions) stop at the first problem they find. | Code | Field | Refused when | |------|-------|--------------| | `InvalidBody` | — | The body is not a JSON object (a list, a bare value), or a `PUT` of contract terms sends no attribute. | | `UnknownField` | the attribute | The endpoint does not take the attribute; one entry per attribute. | | `NotSingleValue` | the attribute | On a `PUT` of contract terms, a list sent for a term that takes one value (every term but `metadata` and `external_references`). On a link, the same list answers the term's own code. | | `Required` | `parent_document_id`, `relation_type`, `external_references[i].system`, `external_references[i].id` | A first link without `parent_document_id` (a pending link that was removed meanwhile) or `relation_type`; an `external_references` item without `system` or `id` (`i` is its place in the list sent, from 0). | | `Invalid` | the field | The value is not of the field's type or form: `status` not one of the four; `currency` not three letters; `auto_renew` not a boolean; `owner_id` not a user id; `value` not a number; `counterparty_name` holding HTML; `metadata` not an object; `external_references` not a list, an item's `system` (`external_references[i].system`) with characters other than letters, digits, `_`, `-` and `.`, or its `id` (`external_references[i].id`) with an emoji or another 4-byte character; `parent_document_id` not text; `relation_type` not `amendment`, `renewal` or `termination`; `term` not a whole number from 1; `reason` not text. On the list: a filter sent as a list, a `status` not one of the four, `ending_within_days` not a whole number. | | `TooSmall`, `TooBig` | the field | `value` below 0 or above 9999999999999.99; `renewal_term_months` outside 1 to 600; `notice_period_days` (and the list's `ending_within_days`) outside 0 to 3650. | | `TooLong` | the field | `counterparty_name` over 255 characters, counting each emoji as stored (several characters); `counterparty_taxpayer_id` over 20; `reason` over 500; `external_references[i].system` over 64 or `external_references[i].id` over 255. | | `TooMany` | `external_references` | More than 20 items. | | `TooManyDecimals` | `value` | More than two decimal places. | | `InvalidDate` | `start_date`, `end_date`, `updated_at`; the list's `end_date_from`, `end_date_to` | Not a date as `YYYY-MM-DD` (an impossible one such as `2026-02-30` included); `updated_at` not a date and time as the API writes it (`2026-10-23T12:00:00Z`, or with an offset). | | `DateTooEarly` | `start_date`, `end_date` | Before the year 1000. | | `DateTooLate` | `end_date` | A renewal by the term would pass 9999-12-31. | | `EndBeforeStart` | `end_date` | Before `start_date`. | | `InvalidTaxpayerId` | `counterparty_taxpayer_id` | Not a valid CPF or CNPJ (formatted or not). | | `NotAMember` | `owner_id` | Not an active member of the workspace. | | `TooManyKeys` | `metadata` | More than 50 keys. | | `InvalidKey` | `metadata` | A key that is not 1 to 40 letters, digits, `_`, `-` or `.`. | | `InvalidValue` | `metadata` | A value that is not plain text of at most 500 characters. | | `NumberUnderDateKey` | `metadata` | A number under a key ending in `_at`, or `stuck_since`, which the API would return as a date. | | `ListKeys` | `metadata` | Keys `0`, `1`, `2`…, which would read back as a list. | | `DuplicateReference` | `external_references` | Two items the database takes for one (accents, `ß` and `ss`); items equal case aside are merged, not refused. | | `StatusNotSettable` | `status` | A tracked contract sent `expired` or `terminated`, or a closed one sent `draft` or `active`: a contract is closed by terminating it and reopened by renewing it. | | `CannotReturnToDraft` | `status` | An `active` contract sent `draft`. | | `ExpiredNeedsPastEndDate` | `end_date` | New terms with status `expired` and no `end_date` before today. | | `TerminatedNeedsEndDate` | `end_date` | New terms with status `terminated` and no `end_date`. | | `UnsignableDocument` | — | New terms, or a first link, on a cancelled, declined or expired document. Terms or a link already on one stay editable. | | `DocumentIsAmendment` | — | Terms of its own for a document linked as an amendment: its terms are the changes it makes. | | `DocumentHasOwnTerms` | — | A link for a document that tracks a contract of its own: a document is a contract or an amendment, never both. | | `ParentHasNoTerms` | `parent_document_id` | The parent named has no contract terms, nor amends a contract that has. | | `ParentTerminated` | `parent_document_id` | The parent contract is terminated. | | `NoTermChanged` | — | An `amendment` that changes no term, also by dropping its last one. A `renewal` or `termination` may change none. | | `TermNotAllowed` | the term | A term the relation type cannot change: a `renewal` changes only `end_date` and `value`, a `termination` only `end_date`. | | `AmendmentApplied` | — | The amendment applied when its document was signed and can no longer change. | | `TermOrEndDateRequired` | — | A renewal sends neither `term` nor `end_date`. | | `TermMismatch` | `term` | Not the contract's current term: the renewal already happened. | | `ContractTerminated` | — | A renewal of a terminated contract. | | `EndDateRequired` | `end_date` | A renewal by the term, or a `renewal` link without `end_date`, of a contract without an end date. | | `EndDateNotAfterCurrent` | `end_date` | A renewal, or a `renewal` link, to a date not after the current end date. | | `AlreadyTerminated` | — | A termination of a contract already terminated. | A body that is not valid JSON is refused before it reaches these endpoints, with the platform's `400` and no `errors`. A link is also checked against what applying it today would give the parent, so a delta the contract would refuse comes back with the contract's own codes.
- `401`: Missing or invalid credentials.
- `403`: The workspace's plan does not include contract tracking.
- `404`: The document is not in the workspace, the `parent_document_id` names a document outside it, or `parent_document_id: null` is sent for a document that is not an amendment.
- `409`: The write sent the `updated_at` it read the contract (or the amendment link) with, and the contract changed since: by another user, an integration or the hourly run. Nothing was written. Read it again, check the change still applies, and send it with the new `updated_at`.
- `500`: Unexpected server error.

## Changes

- **2026-10-28** `3672991376b6`: 1 info
  - endpoint added

[Operation history](https://skmtc.dev/assinafy/apis/assinafy-api-reference/changes/v1/accounts/:accountId/documents/:documentId/contract/put.md)

---

[API](https://skmtc.dev/assinafy/apis/assinafy-api-reference.md) - [All operations](https://skmtc.dev/assinafy/apis/assinafy-api-reference/llms.txt) - [OpenAPI document](https://skmtc.dev/assinafy/apis/assinafy-api-reference/revisions/3672991376b6?raw)
