openapi: 3.1.0
info:
  title: Unforged Sig API
  version: 2026-10-14
  summary: "Signing: packages of documents sent to people to sign, with proof of who signed."
  description: |
    Sig's routes, under `/sig/`: send documents to be signed, follow each package as people sign,
    and download the signed copies and their certificate. Every route answers 404 if Sig has been
    switched off for your organisation. A signed document costs the same through
    the API as in the app. The platform routes (keys, the audit log, billing) are in the platform
    document.

    **Authentication.** Send `Authorization: Bearer <API key>`, and treat the whole key as a secret.

    **Regions.** Your organisation's data lives in its home region, fixed at creation. Call
    `https://api.unforged.sh` or your region's host, `https://<region>.api.unforged.sh`; another
    region's host answers 421 `wrong_region`.

    **Access.** Every route needs one permission, shown as `x-permission`; without it the answer is
    403 `permission_denied`. Ids are scoped to your organisation. Permissions and default roles:
    https://unforged.ai/security#access.

    **Errors** are RFC 9457 `application/problem+json` documents with a stable `code` (see
    `Problem`). Every response carries `X-Request-Id`.

    **Versioning.** The API version is a date, sent as `Unforged-Version: YYYY-MM-DD`. Your
    organisation is pinned to the version current when it was created; the header overrides the pin
    for one request. An unknown version is 400 `invalid_version`.

    Additive changes never create a version: new endpoints, optional parameters, response fields
    and open enum values reach every version. **Clients must ignore unknown fields.** Breaking
    changes ship only in a new dated version.

    **Deprecation.** A version is supported for 24 months after it is replaced. Responses on a
    deprecated version carry `Deprecation`, `Sunset` and a `Link` to the upgrade guide.
  contact:
    name: Unforged
    url: https://unforged.sh
servers:
  - url: https://api.unforged.sh
security:
  - apiKey: []
  - userToken: []
tags:
  - name: packages
    description: Send documents to be signed, and follow them until everyone has signed.
  - name: documents
    description: The documents as sent, the signed copies and the certificate of signing.
  - name: webhooks
    description: Hear when a package is sent, viewed, signed, declined, voided or expires.
  - name: settings
    description: Sig's settings and the month's usage.
  - name: sig
    description: Signing.
paths:
  /sig/settings:
    get:
      tags:
        - settings
      operationId: getSigSettings
      x-permission: sig:packages:read
      summary: Read Sig's settings
      description: Whether completed packages are filed in Folio and whether signatures are checked with Nib. Both are off until the organisation turns them on.
      parameters:
        - $ref: "#/components/parameters/UnforgedVersion"
        - $ref: "#/components/parameters/UnforgedOrg"
      responses:
        "200":
          description: The settings.
          headers:
            X-Request-Id:
              $ref: "#/components/headers/RequestId"
            Unforged-Version:
              $ref: "#/components/headers/UnforgedVersion"
            Unforged-Latest-Version:
              $ref: "#/components/headers/UnforgedLatestVersion"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SigSettings"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"
        "421":
          $ref: "#/components/responses/WrongRegion"
        "429":
          $ref: "#/components/responses/RateLimited"
        default:
          $ref: "#/components/responses/Internal"
    put:
      tags:
        - settings
      operationId: setSigSettings
      x-permission: sig:settings:manage
      summary: Change Sig's settings
      description: Turning on Folio filing needs Folio for the organisation (else `400`).
      parameters:
        - $ref: "#/components/parameters/UnforgedVersion"
        - $ref: "#/components/parameters/UnforgedOrg"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/SigSettingsRequest"
      responses:
        "200":
          description: Changed.
          headers:
            X-Request-Id:
              $ref: "#/components/headers/RequestId"
            Unforged-Version:
              $ref: "#/components/headers/UnforgedVersion"
            Unforged-Latest-Version:
              $ref: "#/components/headers/UnforgedLatestVersion"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SigSettings"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"
        "413":
          $ref: "#/components/responses/TooLarge"
        "421":
          $ref: "#/components/responses/WrongRegion"
        "429":
          $ref: "#/components/responses/RateLimited"
        default:
          $ref: "#/components/responses/Internal"
  /sig/settings/return:
    get:
      tags:
        - settings
      operationId: getSigReturn
      x-permission: sig:packages:read
      summary: Read where signers go once they've finished
      description: The link signers are sent back to once they've signed or declined, unless a package has its own `return_url`, and the origins a return link may use besides your verified domains.
      parameters:
        - $ref: "#/components/parameters/UnforgedVersion"
        - $ref: "#/components/parameters/UnforgedOrg"
      responses:
        "200":
          description: The settings.
          headers:
            X-Request-Id:
              $ref: "#/components/headers/RequestId"
            Unforged-Version:
              $ref: "#/components/headers/UnforgedVersion"
            Unforged-Latest-Version:
              $ref: "#/components/headers/UnforgedLatestVersion"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SigReturnSettings"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"
        "421":
          $ref: "#/components/responses/WrongRegion"
        "429":
          $ref: "#/components/responses/RateLimited"
        default:
          $ref: "#/components/responses/Internal"
    put:
      tags:
        - settings
      operationId: setSigReturn
      x-permission: sig:settings:manage
      summary: Change where signers go once they've finished
      description: A return link must start with `https://` and be on one of your verified domains, or on one of the `origins` you list here (`400` otherwise). Signers arrive with `package_id`, `signer_id` and `outcome` (`signed`, `approved` or `declined`) added to the link.
      parameters:
        - $ref: "#/components/parameters/UnforgedVersion"
        - $ref: "#/components/parameters/UnforgedOrg"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/SigReturnRequest"
      responses:
        "200":
          description: Changed.
          headers:
            X-Request-Id:
              $ref: "#/components/headers/RequestId"
            Unforged-Version:
              $ref: "#/components/headers/UnforgedVersion"
            Unforged-Latest-Version:
              $ref: "#/components/headers/UnforgedLatestVersion"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SigReturnSettings"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"
        "413":
          $ref: "#/components/responses/TooLarge"
        "421":
          $ref: "#/components/responses/WrongRegion"
        "429":
          $ref: "#/components/responses/RateLimited"
        default:
          $ref: "#/components/responses/Internal"
  /sig/usage:
    get:
      tags:
        - settings
      operationId: getSigUsage
      x-permission: org:read
      summary: The month's signing and what it costs
      description: "One UTC month (`period`: `current`, the default, or `YYYY-MM`): packages sent and completed, documents signed and the estimated charge so far, in the currency your organisation is billed for Sig in. Only signed documents are charged."
      parameters:
        - $ref: "#/components/parameters/UnforgedVersion"
        - $ref: "#/components/parameters/UnforgedOrg"
        - name: period
          in: query
          schema:
            type: string
            pattern: ^(current|\d{4}-\d{2})$
            default: current
          example: 2026-10
      responses:
        "200":
          description: The month's usage.
          headers:
            X-Request-Id:
              $ref: "#/components/headers/RequestId"
            Unforged-Version:
              $ref: "#/components/headers/UnforgedVersion"
            Unforged-Latest-Version:
              $ref: "#/components/headers/UnforgedLatestVersion"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SigUsage"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"
        "421":
          $ref: "#/components/responses/WrongRegion"
        "429":
          $ref: "#/components/responses/RateLimited"
        default:
          $ref: "#/components/responses/Internal"
  /sig/counts:
    get:
      tags:
        - packages
      operationId: getSigCounts
      x-permission: sig:packages:read
      summary: How many packages are where
      description: The packages with signers now, the signatures waiting for someone in your organisation to review them, the drafts, and the packages fully signed this UTC month.
      parameters:
        - $ref: "#/components/parameters/UnforgedVersion"
        - $ref: "#/components/parameters/UnforgedOrg"
      responses:
        "200":
          description: The counts.
          headers:
            X-Request-Id:
              $ref: "#/components/headers/RequestId"
            Unforged-Version:
              $ref: "#/components/headers/UnforgedVersion"
            Unforged-Latest-Version:
              $ref: "#/components/headers/UnforgedLatestVersion"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SigCounts"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"
        "421":
          $ref: "#/components/responses/WrongRegion"
        "429":
          $ref: "#/components/responses/RateLimited"
        default:
          $ref: "#/components/responses/Internal"
  /sig/packages:
    get:
      tags:
        - packages
      operationId: listSigPackages
      x-permission: sig:packages:read
      summary: List packages
      description: Newest first, 50 a page (`limit` up to 200); `next_cursor` continues the list. A package past its expiry shows as `expired`.
      parameters:
        - $ref: "#/components/parameters/UnforgedVersion"
        - $ref: "#/components/parameters/UnforgedOrg"
        - name: status
          in: query
          required: false
          description: "One status, or several separated by commas, such as `sent,partly_signed`: draft, sent, partly_signed, completed, declined, voided or expired."
          schema:
            type: string
          example: sent,partly_signed
        - name: q
          in: query
          required: false
          description: Words to look for in the package's title or reference, or in a signer's name or email. A title or name close to them is found too, so a small typo still finds it.
          schema:
            type: string
            maxLength: 200
        - name: sent_by
          in: query
          required: false
          description: Only the packages this person sent, by the `created_by` the list gives.
          schema:
            type: string
        - name: review
          in: query
          required: false
          description: "`waiting` lists only the packages with a signature waiting for someone in your organisation to review it."
          schema:
            type: string
            enum:
              - waiting
        - name: cursor
          in: query
          required: false
          schema:
            type: string
        - name: limit
          in: query
          required: false
          schema:
            type: integer
            minimum: 1
            maximum: 200
      responses:
        "200":
          description: A page of packages.
          headers:
            X-Request-Id:
              $ref: "#/components/headers/RequestId"
            Unforged-Version:
              $ref: "#/components/headers/UnforgedVersion"
            Unforged-Latest-Version:
              $ref: "#/components/headers/UnforgedLatestVersion"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SigPackagePage"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"
        "421":
          $ref: "#/components/responses/WrongRegion"
        "429":
          $ref: "#/components/responses/RateLimited"
        default:
          $ref: "#/components/responses/Internal"
    post:
      tags:
        - packages
      operationId: createSigPackage
      x-permission: sig:packages:send
      summary: Create a package and send it
      description: "One call: each document as a `file` part (PDF, PNG or JPEG; up to 20, 25 MB each and 50 MB together) and the package as a `package` part (JSON). It is sent at once unless `send` is false, and each signer whose turn it is gets an email with their own link; the response carries those links, once. Signers with a lower `order` sign first; signers sharing an order sign in any order. We find where each signer signs, initials and dates from the documents themselves, and give each place to the signer it names (a `role` helps); a scan takes a little longer, while we read it. Pass `fields` to place them yourself."
      parameters:
        - $ref: "#/components/parameters/UnforgedVersion"
        - $ref: "#/components/parameters/UnforgedOrg"
        - $ref: "#/components/parameters/IdempotencyKey"
      requestBody:
        required: true
        content:
          multipart/form-data:
            schema:
              type: object
              required:
                - file
                - package
              properties:
                file:
                  type: array
                  items:
                    type: string
                    format: binary
                package:
                  $ref: "#/components/schemas/SigPackageRequest"
            encoding:
              package:
                contentType: application/json
      responses:
        "201":
          description: Created (and sent, unless `send` was false).
          headers:
            X-Request-Id:
              $ref: "#/components/headers/RequestId"
            Unforged-Version:
              $ref: "#/components/headers/UnforgedVersion"
            Unforged-Latest-Version:
              $ref: "#/components/headers/UnforgedLatestVersion"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SigPackage"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"
        "409":
          $ref: "#/components/responses/Conflict"
        "413":
          $ref: "#/components/responses/TooLarge"
        "415":
          $ref: "#/components/responses/UnsupportedMedia"
        "421":
          $ref: "#/components/responses/WrongRegion"
        "429":
          $ref: "#/components/responses/RateLimited"
        default:
          $ref: "#/components/responses/Internal"
  /sig/packages/{package_id}:
    get:
      tags:
        - packages
      operationId: getSigPackage
      x-permission: sig:packages:read
      summary: Read a package
      description: With its documents, signers and every event (who opened it, from which address and browser, and when).
      parameters:
        - $ref: "#/components/parameters/UnforgedVersion"
        - $ref: "#/components/parameters/UnforgedOrg"
        - name: package_id
          in: path
          required: true
          description: The package's id.
          schema:
            type: string
      responses:
        "200":
          description: The package.
          headers:
            X-Request-Id:
              $ref: "#/components/headers/RequestId"
            Unforged-Version:
              $ref: "#/components/headers/UnforgedVersion"
            Unforged-Latest-Version:
              $ref: "#/components/headers/UnforgedLatestVersion"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SigPackage"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"
        "421":
          $ref: "#/components/responses/WrongRegion"
        "429":
          $ref: "#/components/responses/RateLimited"
        default:
          $ref: "#/components/responses/Internal"
  /sig/packages/{package_id}/send:
    post:
      tags:
        - packages
      operationId: sendSigPackage
      x-permission: sig:packages:send
      summary: Send a draft package
      description: Emails each signer whose turn it is; the response carries their links, once. `409` once it has been sent.
      parameters:
        - $ref: "#/components/parameters/UnforgedVersion"
        - $ref: "#/components/parameters/UnforgedOrg"
        - $ref: "#/components/parameters/IdempotencyKey"
        - name: package_id
          in: path
          required: true
          description: The package's id.
          schema:
            type: string
      requestBody:
        required: false
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/SigSendRequest"
      responses:
        "200":
          description: The package.
          headers:
            X-Request-Id:
              $ref: "#/components/headers/RequestId"
            Unforged-Version:
              $ref: "#/components/headers/UnforgedVersion"
            Unforged-Latest-Version:
              $ref: "#/components/headers/UnforgedLatestVersion"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SigPackage"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "402":
          $ref: "#/components/responses/PaymentRequired"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"
        "409":
          $ref: "#/components/responses/Conflict"
        "421":
          $ref: "#/components/responses/WrongRegion"
        "429":
          $ref: "#/components/responses/RateLimited"
        default:
          $ref: "#/components/responses/Internal"
  /sig/packages/{package_id}/remind:
    post:
      tags:
        - packages
      operationId: remindSigPackage
      x-permission: sig:packages:send
      summary: Remind the signers
      description: Emails everyone who has a link and hasn't signed, at most once an hour each. Their earlier links keep working. A reminder never counts as a new signed document.
      parameters:
        - $ref: "#/components/parameters/UnforgedVersion"
        - $ref: "#/components/parameters/UnforgedOrg"
        - $ref: "#/components/parameters/IdempotencyKey"
        - name: package_id
          in: path
          required: true
          description: The package's id.
          schema:
            type: string
      responses:
        "200":
          description: The package.
          headers:
            X-Request-Id:
              $ref: "#/components/headers/RequestId"
            Unforged-Version:
              $ref: "#/components/headers/UnforgedVersion"
            Unforged-Latest-Version:
              $ref: "#/components/headers/UnforgedLatestVersion"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SigPackage"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"
        "409":
          $ref: "#/components/responses/Conflict"
        "421":
          $ref: "#/components/responses/WrongRegion"
        "429":
          $ref: "#/components/responses/RateLimited"
        default:
          $ref: "#/components/responses/Internal"
  /sig/packages/{package_id}/copy:
    post:
      tags:
        - packages
      operationId: copySigPackage
      x-permission: sig:packages:send
      summary: Start again from a package
      description: Makes a new draft with the same documents, people and places to sign, so you can fix what needs fixing and send it again. Use it after someone declines, or when a package was voided or ran out of time. The original stays as it was.
      parameters:
        - $ref: "#/components/parameters/UnforgedVersion"
        - $ref: "#/components/parameters/UnforgedOrg"
        - $ref: "#/components/parameters/IdempotencyKey"
        - name: package_id
          in: path
          required: true
          description: The package to start from. It must have been sent.
          schema:
            type: string
      responses:
        "201":
          description: The new draft.
          headers:
            X-Request-Id:
              $ref: "#/components/headers/RequestId"
            Unforged-Version:
              $ref: "#/components/headers/UnforgedVersion"
            Unforged-Latest-Version:
              $ref: "#/components/headers/UnforgedLatestVersion"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SigPackage"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"
        "409":
          $ref: "#/components/responses/Conflict"
        "421":
          $ref: "#/components/responses/WrongRegion"
        "429":
          $ref: "#/components/responses/RateLimited"
        default:
          $ref: "#/components/responses/Internal"
  /sig/packages/{package_id}/void:
    post:
      tags:
        - packages
      operationId: voidSigPackage
      x-permission: sig:packages:send
      summary: Void a package
      description: Stops a draft or open package for good, with an optional reason. Signers who had a link are told there's nothing to sign.
      parameters:
        - $ref: "#/components/parameters/UnforgedVersion"
        - $ref: "#/components/parameters/UnforgedOrg"
        - $ref: "#/components/parameters/IdempotencyKey"
        - name: package_id
          in: path
          required: true
          description: The package's id.
          schema:
            type: string
      requestBody:
        required: false
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/SigVoidRequest"
      responses:
        "200":
          description: The package.
          headers:
            X-Request-Id:
              $ref: "#/components/headers/RequestId"
            Unforged-Version:
              $ref: "#/components/headers/UnforgedVersion"
            Unforged-Latest-Version:
              $ref: "#/components/headers/UnforgedLatestVersion"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SigPackage"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"
        "409":
          $ref: "#/components/responses/Conflict"
        "421":
          $ref: "#/components/responses/WrongRegion"
        "429":
          $ref: "#/components/responses/RateLimited"
        default:
          $ref: "#/components/responses/Internal"
  /sig/packages/{package_id}/fields:
    put:
      tags:
        - packages
      operationId: setSigFields
      x-permission: sig:packages:send
      summary: Change where signers sign
      description: "Replaces the package's fields with these, until someone has signed. Send them all: a field you leave out is removed."
      parameters:
        - $ref: "#/components/parameters/UnforgedVersion"
        - $ref: "#/components/parameters/UnforgedOrg"
        - name: package_id
          in: path
          required: true
          description: The package's id.
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/SigFieldsRequest"
      responses:
        "200":
          description: The package.
          headers:
            X-Request-Id:
              $ref: "#/components/headers/RequestId"
            Unforged-Version:
              $ref: "#/components/headers/UnforgedVersion"
            Unforged-Latest-Version:
              $ref: "#/components/headers/UnforgedLatestVersion"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SigPackage"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"
        "409":
          $ref: "#/components/responses/Conflict"
        "421":
          $ref: "#/components/responses/WrongRegion"
        "429":
          $ref: "#/components/responses/RateLimited"
        default:
          $ref: "#/components/responses/Internal"
  /sig/packages/{package_id}/draft:
    put:
      tags:
        - packages
      operationId: saveSigDraft
      x-permission: sig:packages:send
      summary: Save a draft, and send it when ready
      description: "Saves a draft package as you have it now: its details, everyone on it and the places on its documents. Send them all, as with fields: a person or place you leave out is removed. Keep someone already on the draft by giving their `id`. Until you send it, people may be missing a name or email address. Set `send` to true to send it, once everyone has both (a witness their signer names as they sign can wait)."
      parameters:
        - $ref: "#/components/parameters/UnforgedVersion"
        - $ref: "#/components/parameters/UnforgedOrg"
        - name: package_id
          in: path
          required: true
          description: The package's id.
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/SigDraftRequest"
      responses:
        "200":
          description: The package.
          headers:
            X-Request-Id:
              $ref: "#/components/headers/RequestId"
            Unforged-Version:
              $ref: "#/components/headers/UnforgedVersion"
            Unforged-Latest-Version:
              $ref: "#/components/headers/UnforgedLatestVersion"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SigPackage"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"
        "409":
          $ref: "#/components/responses/Conflict"
        "421":
          $ref: "#/components/responses/WrongRegion"
        "429":
          $ref: "#/components/responses/RateLimited"
        default:
          $ref: "#/components/responses/Internal"
  /sig/packages/{package_id}/signers/{signer_id}:
    patch:
      tags:
        - packages
      operationId: correctSigSigner
      x-permission: sig:packages:send
      summary: Correct a signer
      description: Change the name, email address or mobile number of a signer who hasn't signed. A new address stops their earlier links and, if it is their turn, sends a new one, returned once.
      parameters:
        - $ref: "#/components/parameters/UnforgedVersion"
        - $ref: "#/components/parameters/UnforgedOrg"
        - name: package_id
          in: path
          required: true
          description: The package's id.
          schema:
            type: string
        - name: signer_id
          in: path
          required: true
          description: The signer's id.
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/SigSignerCorrection"
      responses:
        "200":
          description: The package.
          headers:
            X-Request-Id:
              $ref: "#/components/headers/RequestId"
            Unforged-Version:
              $ref: "#/components/headers/UnforgedVersion"
            Unforged-Latest-Version:
              $ref: "#/components/headers/UnforgedLatestVersion"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SigPackage"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"
        "409":
          $ref: "#/components/responses/Conflict"
        "421":
          $ref: "#/components/responses/WrongRegion"
        "429":
          $ref: "#/components/responses/RateLimited"
        default:
          $ref: "#/components/responses/Internal"
  /sig/packages/{package_id}/documents/{document_id}/content:
    get:
      tags:
        - documents
      operationId: getSigDocument
      x-permission: sig:packages:read
      summary: Download a document
      description: The document as it was sent.
      parameters:
        - $ref: "#/components/parameters/UnforgedVersion"
        - $ref: "#/components/parameters/UnforgedOrg"
        - name: package_id
          in: path
          required: true
          description: The package's id.
          schema:
            type: string
        - name: document_id
          in: path
          required: true
          description: The document's id.
          schema:
            type: string
      responses:
        "200":
          description: The document.
          headers:
            X-Request-Id:
              $ref: "#/components/headers/RequestId"
            Unforged-Version:
              $ref: "#/components/headers/UnforgedVersion"
            Unforged-Latest-Version:
              $ref: "#/components/headers/UnforgedLatestVersion"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"
        "421":
          $ref: "#/components/responses/WrongRegion"
        "429":
          $ref: "#/components/responses/RateLimited"
        default:
          $ref: "#/components/responses/Internal"
  /sig/packages/{package_id}/documents/{document_id}/signed:
    get:
      tags:
        - documents
      operationId: getSigSignedDocument
      x-permission: sig:packages:read
      summary: Download a signed copy
      description: The document with everyone's signatures, initials and dates in place. Any later change to the file shows when it's opened. Ready shortly after the last signer signs (the `sig.package.completed` webhook says so); 404 until then.
      parameters:
        - $ref: "#/components/parameters/UnforgedVersion"
        - $ref: "#/components/parameters/UnforgedOrg"
        - name: package_id
          in: path
          required: true
          description: The package's id.
          schema:
            type: string
        - name: document_id
          in: path
          required: true
          description: The document's id.
          schema:
            type: string
      responses:
        "200":
          description: The signed copy (PDF).
          headers:
            X-Request-Id:
              $ref: "#/components/headers/RequestId"
            Unforged-Version:
              $ref: "#/components/headers/UnforgedVersion"
            Unforged-Latest-Version:
              $ref: "#/components/headers/UnforgedLatestVersion"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"
        "421":
          $ref: "#/components/responses/WrongRegion"
        "429":
          $ref: "#/components/responses/RateLimited"
        default:
          $ref: "#/components/responses/Internal"
  /sig/packages/{package_id}/certificate:
    get:
      tags:
        - documents
      operationId: getSigCertificate
      x-permission: sig:packages:read
      summary: Download the certificate of signing
      description: Who signed, when, where, on what device and how we know, with each document's fingerprint before signing and of its signed copy. The same pages are at the end of every signed copy. Ready with the signed copies; 404 until then.
      parameters:
        - $ref: "#/components/parameters/UnforgedVersion"
        - $ref: "#/components/parameters/UnforgedOrg"
        - name: package_id
          in: path
          required: true
          description: The package's id.
          schema:
            type: string
      responses:
        "200":
          description: The certificate (PDF).
          headers:
            X-Request-Id:
              $ref: "#/components/headers/RequestId"
            Unforged-Version:
              $ref: "#/components/headers/UnforgedVersion"
            Unforged-Latest-Version:
              $ref: "#/components/headers/UnforgedLatestVersion"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"
        "421":
          $ref: "#/components/responses/WrongRegion"
        "429":
          $ref: "#/components/responses/RateLimited"
        default:
          $ref: "#/components/responses/Internal"
  /sig/check:
    post:
      tags:
        - documents
      operationId: checkSigCopy
      x-permission: sig:packages:read
      summary: Check a signed copy
      description: Send a signed copy or certificate as the request body. The answer says whether anything in it has changed since it was signed, when it was time-stamped, and which of your packages it came from. No `Idempotency-Key` is needed.
      parameters:
        - $ref: "#/components/parameters/UnforgedVersion"
        - $ref: "#/components/parameters/UnforgedOrg"
      requestBody:
        required: true
        content:
          application/pdf:
            schema:
              type: string
              format: binary
      responses:
        "200":
          description: What the check found.
          headers:
            X-Request-Id:
              $ref: "#/components/headers/RequestId"
            Unforged-Version:
              $ref: "#/components/headers/UnforgedVersion"
            Unforged-Latest-Version:
              $ref: "#/components/headers/UnforgedLatestVersion"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SigCopyCheck"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "413":
          $ref: "#/components/responses/TooLarge"
        "421":
          $ref: "#/components/responses/WrongRegion"
        "429":
          $ref: "#/components/responses/RateLimited"
        default:
          $ref: "#/components/responses/Internal"
  /sig/webhooks:
    get:
      tags:
        - webhooks
      operationId: listSigWebhooks
      x-permission: sig:settings:manage
      summary: List Sig's webhook endpoints
      description: Sig's endpoints only.
      parameters:
        - $ref: "#/components/parameters/UnforgedVersion"
        - $ref: "#/components/parameters/UnforgedOrg"
      responses:
        "200":
          description: The endpoints.
          headers:
            X-Request-Id:
              $ref: "#/components/headers/RequestId"
            Unforged-Version:
              $ref: "#/components/headers/UnforgedVersion"
            Unforged-Latest-Version:
              $ref: "#/components/headers/UnforgedLatestVersion"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SigWebhookList"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"
        "421":
          $ref: "#/components/responses/WrongRegion"
        "429":
          $ref: "#/components/responses/RateLimited"
        default:
          $ref: "#/components/responses/Internal"
    post:
      tags:
        - webhooks
      operationId: createSigWebhook
      x-permission: sig:settings:manage
      summary: Create a Sig webhook endpoint
      description: "Like `POST /nib/webhooks` (the same signing, retries, delivery log and connection test), for Sig's events: `sig.package.sent`, `sig.signer.viewed`, `sig.signer.signed`, `sig.package.completed`, `sig.package.declined`, `sig.package.voided`, `sig.package.expired`. Each event carries the `package_id`; one about a signer carries their `signer_id` too, and `sig.signer.signed` and `sig.package.declined` say what they did in `outcome` (`signed`, `approved` or `declined`), the same as a package's return link. The secret is shown once. No `Idempotency-Key`: a retry creates another endpoint."
      parameters:
        - $ref: "#/components/parameters/UnforgedVersion"
        - $ref: "#/components/parameters/UnforgedOrg"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/SigWebhookCreate"
      responses:
        "201":
          description: Created.
          headers:
            X-Request-Id:
              $ref: "#/components/headers/RequestId"
            Unforged-Version:
              $ref: "#/components/headers/UnforgedVersion"
            Unforged-Latest-Version:
              $ref: "#/components/headers/UnforgedLatestVersion"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SigWebhookCreated"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"
        "409":
          $ref: "#/components/responses/Conflict"
        "421":
          $ref: "#/components/responses/WrongRegion"
        "429":
          $ref: "#/components/responses/RateLimited"
        default:
          $ref: "#/components/responses/Internal"
  /sig/webhooks/{webhook_id}:
    delete:
      tags:
        - webhooks
      operationId: deleteSigWebhook
      x-permission: sig:settings:manage
      summary: Delete a Sig webhook endpoint
      description: "`404` for an unknown endpoint and for another product's."
      parameters:
        - $ref: "#/components/parameters/UnforgedVersion"
        - $ref: "#/components/parameters/UnforgedOrg"
        - name: webhook_id
          in: path
          required: true
          description: The endpoint's id.
          schema:
            type: string
      responses:
        "204":
          description: Deleted.
          headers:
            X-Request-Id:
              $ref: "#/components/headers/RequestId"
            Unforged-Version:
              $ref: "#/components/headers/UnforgedVersion"
            Unforged-Latest-Version:
              $ref: "#/components/headers/UnforgedLatestVersion"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"
        "421":
          $ref: "#/components/responses/WrongRegion"
        "429":
          $ref: "#/components/responses/RateLimited"
        default:
          $ref: "#/components/responses/Internal"
components:
  securitySchemes:
    apiKey:
      type: http
      scheme: bearer
      bearerFormat: unf_<env>_<opaque>
      description: An API key holding the route's permission.
    sessionToken:
      type: http
      scheme: bearer
      bearerFormat: unf_ses_<token>
      description: "A client session token: `GET /nib/live` and streamed `POST /nib/checks` only."
    userToken:
      type: http
      scheme: bearer
      bearerFormat: JWT
      description: A signed-in dashboard user's session token.
    internalToken:
      type: http
      scheme: bearer
      description: Reserved for Unforged.
    sessionSubprotocol:
      type: apiKey
      in: header
      name: Sec-WebSocket-Protocol
      description: "A session token as a WebSocket subprotocol: `unforged.session, <token>`."
  headers:
    RequestId:
      description: The request id; also in problem bodies.
      schema:
        type: string
        maxLength: 128
      example: req_01j9x3m8v7k2q4r5s6t7u8v9w0
    RetryAfter:
      description: Seconds to wait before retrying.
      schema:
        type: integer
        minimum: 0
      example: 1
    IdempotentReplayed:
      description: "`true` on a replayed response."
      schema:
        type: string
        const: "true"
    NoStore:
      description: The body holds a secret shown once.
      schema:
        type: string
        const: no-store
    WwwAuthenticate:
      schema:
        type: string
        const: Bearer
    UnforgedVersion:
      description: The API version used.
      schema:
        type: string
        pattern: ^\d{4}-\d{2}-\d{2}$
      example: 2026-09-27
    UnforgedLatestVersion:
      description: The latest API version.
      schema:
        type: string
        pattern: ^\d{4}-\d{2}-\d{2}$
      example: 2026-09-27
    FolioAcceptRanges:
      description: A document may be read in parts.
      schema:
        type: string
        const: bytes
    FolioETag:
      description: The document's SHA-256, quoted. A record never changes; name it in `If-Range`.
      schema:
        type: string
      example: '"9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08"'
    FolioContentRange:
      description: The part sent, and the document's size (`bytes */<size>` on a `416`).
      schema:
        type: string
      example: bytes 0-1048575/734003200
  parameters:
    FolioId:
      name: id
      in: path
      required: true
      description: A record, queued filing or export id.
      schema:
        type: string
      example: rec_01J9X3M8V7K2Q4R5S6T7U8V9W0
    FolioReasonId:
      name: Unforged-Reason-Id
      in: header
      required: false
      description: An open reason (`POST /folio/reasons`), when the organisation or the folder requires one for this view.
      schema:
        type: string
    FolioReasonCode:
      name: Unforged-Reason-Code
      in: header
      required: false
      description: "Instead of an open reason: a reason code for this one call (one of your organisation's codes). It is opened for exactly the records the call reads and ledgered like any reason."
      schema:
        type: string
      example: internal_review
    FolioReasonText:
      name: Unforged-Reason
      in: header
      required: false
      description: The reason's text, with `Unforged-Reason-Code` (plain ASCII).
      schema:
        type: string
    UnforgedVersion:
      name: Unforged-Version
      in: header
      required: false
      description: API version (`YYYY-MM-DD`) for this request; defaults to your pinned version.
      schema:
        type: string
        pattern: ^\d{4}-\d{2}-\d{2}$
      example: 2026-09-27
    UnforgedOrg:
      name: Unforged-Org
      in: header
      required: false
      description: Act in a child organisation (its id). Needs `org:children:access`.
      schema:
        type: string
        pattern: ^[A-Za-z0-9_]{1,64}$
      example: org_01j9x3m8v7k2q4r5s6t7u8v9w0
    FolioRange:
      name: Range
      in: header
      required: false
      description: "One range of bytes: `bytes=<first>-<last>`, `bytes=<first>-` or `bytes=-<count>` (the last bytes). At most 64 MiB is sent at a time, cut from the end; `Content-Range` says what was sent. Any other form, or several ranges, is answered with the whole document."
      schema:
        type: string
      example: bytes=0-1048575
    FolioIfRange:
      name: If-Range
      required: false
      in: header
      description: The `ETag` of an earlier answer. Anything else, such as a date, is answered with the whole document.
      schema:
        type: string
    IdempotencyKey:
      name: Idempotency-Key
      in: header
      required: true
      description: 1–255 printable ASCII characters, kept 24 hours.
      schema:
        type: string
        minLength: 1
        maxLength: 255
      example: inv-2291
  responses:
    UnsupportedMedia:
      description: "`unsupported_media`."
      headers:
        X-Request-Id:
          $ref: "#/components/headers/RequestId"
        Unforged-Version:
          $ref: "#/components/headers/UnforgedVersion"
        Unforged-Latest-Version:
          $ref: "#/components/headers/UnforgedLatestVersion"
      content:
        application/problem+json:
          schema:
            $ref: "#/components/schemas/Problem"
    FolioPartialContent:
      description: The part of the document a `Range` asked for.
      headers:
        X-Request-Id:
          $ref: "#/components/headers/RequestId"
        Unforged-Version:
          $ref: "#/components/headers/UnforgedVersion"
        Unforged-Latest-Version:
          $ref: "#/components/headers/UnforgedLatestVersion"
        Accept-Ranges:
          $ref: "#/components/headers/FolioAcceptRanges"
        ETag:
          $ref: "#/components/headers/FolioETag"
        Content-Range:
          $ref: "#/components/headers/FolioContentRange"
      content:
        application/octet-stream:
          schema:
            type: string
            format: binary
    FolioRangeNotSatisfiable:
      description: "`invalid_request`: the range starts past the end of the document. `Content-Range` gives its size."
      headers:
        X-Request-Id:
          $ref: "#/components/headers/RequestId"
        Unforged-Version:
          $ref: "#/components/headers/UnforgedVersion"
        Unforged-Latest-Version:
          $ref: "#/components/headers/UnforgedLatestVersion"
        Content-Range:
          $ref: "#/components/headers/FolioContentRange"
      content:
        application/problem+json:
          schema:
            $ref: "#/components/schemas/Problem"
    FolioReasonRequired:
      description: "`reason_required`: a reason is missing, has expired, or is for another context."
      headers:
        X-Request-Id:
          $ref: "#/components/headers/RequestId"
        Unforged-Version:
          $ref: "#/components/headers/UnforgedVersion"
        Unforged-Latest-Version:
          $ref: "#/components/headers/UnforgedLatestVersion"
      content:
        application/problem+json:
          schema:
            $ref: "#/components/schemas/Problem"
    WrongRegion:
      description: "`wrong_region`: call the `api_host` in the body."
      headers:
        X-Request-Id:
          $ref: "#/components/headers/RequestId"
        Unforged-Version:
          $ref: "#/components/headers/UnforgedVersion"
        Unforged-Latest-Version:
          $ref: "#/components/headers/UnforgedLatestVersion"
      content:
        application/problem+json:
          schema:
            $ref: "#/components/schemas/Problem"
          example:
            type: https://unforged.sh/problems/wrong_region
            title: Wrong region
            status: 421
            detail: this organisation's data lives in uk-london; call https://uk-london.api.unforged.sh
            code: wrong_region
            request_id: req_01j9x3m8v7k2q4r5s6t7u8v9w0
            region: uk-london
            api_host: https://uk-london.api.unforged.sh
    Internal:
      description: "`internal`."
      headers:
        X-Request-Id:
          $ref: "#/components/headers/RequestId"
        Unforged-Version:
          $ref: "#/components/headers/UnforgedVersion"
        Unforged-Latest-Version:
          $ref: "#/components/headers/UnforgedLatestVersion"
      content:
        application/problem+json:
          schema:
            $ref: "#/components/schemas/Problem"
    Conflict:
      description: "`conflict`, `idempotency_conflict`, `idempotency_in_progress` or `idempotency_not_replayable`."
      headers:
        X-Request-Id:
          $ref: "#/components/headers/RequestId"
        Unforged-Version:
          $ref: "#/components/headers/UnforgedVersion"
        Unforged-Latest-Version:
          $ref: "#/components/headers/UnforgedLatestVersion"
      content:
        application/problem+json:
          schema:
            $ref: "#/components/schemas/Problem"
          example:
            type: https://unforged.sh/problems/idempotency_conflict
            title: Idempotency key reused with a different request
            status: 409
            detail: this Idempotency-Key was used with a different request
            code: idempotency_conflict
            request_id: req_01j9x3m8v7k2q4r5s6t7u8v9w0
    PaymentRequired:
      description: "`billing_required`: add a payment method in the dashboard."
      headers:
        X-Request-Id:
          $ref: "#/components/headers/RequestId"
        Unforged-Version:
          $ref: "#/components/headers/UnforgedVersion"
        Unforged-Latest-Version:
          $ref: "#/components/headers/UnforgedLatestVersion"
      content:
        application/problem+json:
          schema:
            $ref: "#/components/schemas/Problem"
          example:
            type: https://unforged.sh/problems/billing_required
            title: Billing required
            status: 402
            detail: "add a payment method in the dashboard: /org/billing"
            code: billing_required
            request_id: req_01j9x3m8v7k2q4r5s6t7u8v9w0
    NotFound:
      description: "`not_found`."
      headers:
        X-Request-Id:
          $ref: "#/components/headers/RequestId"
        Unforged-Version:
          $ref: "#/components/headers/UnforgedVersion"
        Unforged-Latest-Version:
          $ref: "#/components/headers/UnforgedLatestVersion"
      content:
        application/problem+json:
          schema:
            $ref: "#/components/schemas/Problem"
          example:
            type: https://unforged.sh/problems/not_found
            title: Not found
            status: 404
            detail: subject not found
            code: not_found
            request_id: req_01j9x3m8v7k2q4r5s6t7u8v9w0
    NotImplemented:
      description: "`not_implemented` or `not_available_in_dev`."
      headers:
        X-Request-Id:
          $ref: "#/components/headers/RequestId"
        Unforged-Version:
          $ref: "#/components/headers/UnforgedVersion"
        Unforged-Latest-Version:
          $ref: "#/components/headers/UnforgedLatestVersion"
      content:
        application/problem+json:
          schema:
            $ref: "#/components/schemas/Problem"
    Unauthorized:
      description: "`unauthorized`: missing, invalid or expired credential."
      headers:
        X-Request-Id:
          $ref: "#/components/headers/RequestId"
        Unforged-Version:
          $ref: "#/components/headers/UnforgedVersion"
        Unforged-Latest-Version:
          $ref: "#/components/headers/UnforgedLatestVersion"
        WWW-Authenticate:
          $ref: "#/components/headers/WwwAuthenticate"
      content:
        application/problem+json:
          schema:
            $ref: "#/components/schemas/Problem"
          example:
            type: https://unforged.sh/problems/unauthorized
            title: Unauthorized
            status: 401
            detail: invalid or revoked API key
            code: unauthorized
            request_id: req_01j9x3m8v7k2q4r5s6t7u8v9w0
    Unavailable:
      description: "`busy`, `not_ready`, `org_moving` or `storage_provisioning` (your organisation's storage is still being set up); retry after `Retry-After`."
      headers:
        X-Request-Id:
          $ref: "#/components/headers/RequestId"
        Unforged-Version:
          $ref: "#/components/headers/UnforgedVersion"
        Unforged-Latest-Version:
          $ref: "#/components/headers/UnforgedLatestVersion"
        Retry-After:
          $ref: "#/components/headers/RetryAfter"
      content:
        application/problem+json:
          schema:
            $ref: "#/components/schemas/Problem"
          example:
            type: https://unforged.sh/problems/busy
            title: Service busy
            status: 503
            detail: no capacity became free in time; the check was accepted as job inv-2291
            code: busy
            request_id: req_01j9x3m8v7k2q4r5s6t7u8v9w0
            job_id: inv-2291
            status_url: /nib/checks/inv-2291
    Forbidden:
      description: "`permission_denied`, `org_suspended`, or `address_not_allowed` (your organisation only accepts requests from the addresses it has listed)."
      headers:
        X-Request-Id:
          $ref: "#/components/headers/RequestId"
        Unforged-Version:
          $ref: "#/components/headers/UnforgedVersion"
        Unforged-Latest-Version:
          $ref: "#/components/headers/UnforgedLatestVersion"
      content:
        application/problem+json:
          schema:
            $ref: "#/components/schemas/Problem"
          example:
            type: https://unforged.sh/problems/permission_denied
            title: Forbidden
            status: 403
            detail: requires nib:settings:manage
            code: permission_denied
            request_id: req_01j9x3m8v7k2q4r5s6t7u8v9w0
    TooLarge:
      description: "`too_large`."
      headers:
        X-Request-Id:
          $ref: "#/components/headers/RequestId"
        Unforged-Version:
          $ref: "#/components/headers/UnforgedVersion"
        Unforged-Latest-Version:
          $ref: "#/components/headers/UnforgedLatestVersion"
      content:
        application/problem+json:
          schema:
            $ref: "#/components/schemas/Problem"
    RateLimited:
      description: "`rate_limited` or `quota_exceeded`."
      headers:
        X-Request-Id:
          $ref: "#/components/headers/RequestId"
        Unforged-Version:
          $ref: "#/components/headers/UnforgedVersion"
        Unforged-Latest-Version:
          $ref: "#/components/headers/UnforgedLatestVersion"
        Retry-After:
          $ref: "#/components/headers/RetryAfter"
      content:
        application/problem+json:
          schema:
            $ref: "#/components/schemas/Problem"
          example:
            type: https://unforged.sh/problems/rate_limited
            title: Rate limit exceeded
            status: 429
            detail: too many requests; retry after 1 s
            code: rate_limited
            request_id: req_01j9x3m8v7k2q4r5s6t7u8v9w0
    BadRequest:
      description: "`invalid_request`, `invalid_version`, `idempotency_key_required` or `invalid_signature`."
      headers:
        X-Request-Id:
          $ref: "#/components/headers/RequestId"
        Unforged-Version:
          $ref: "#/components/headers/UnforgedVersion"
        Unforged-Latest-Version:
          $ref: "#/components/headers/UnforgedLatestVersion"
      content:
        application/problem+json:
          schema:
            $ref: "#/components/schemas/Problem"
          example:
            type: https://unforged.sh/problems/invalid_request
            title: Invalid request
            status: 400
            detail: subject_reference must be a valid id ([A-Za-z0-9._-]{1,128})
            code: invalid_request
            request_id: req_01j9x3m8v7k2q4r5s6t7u8v9w0
  schemas:
    SigPackageRequest:
      type: object
      additionalProperties: false
      properties:
        title:
          type: string
          maxLength: 200
          description: Defaults to the first document's name, or for a draft with no signers, its heading.
        message:
          type: string
          maxLength: 2000
          description: Shown to every signer in the email and on the page.
        reference:
          type: string
          maxLength: 200
          description: Your own id for the package.
        signers:
          type: array
          maxItems: 20
          description: Everyone on the package. Leave it out of a draft (`send` false) and we read the documents and add the people they name, for you to fill in before sending.
          items:
            $ref: "#/components/schemas/SigSignerRequest"
        require_location:
          type: boolean
          default: false
          description: Each signer shares their device's location to sign. Without this, the rough area of their IP address is recorded.
        require_camera:
          type: boolean
          default: false
          description: Take a short video of each signer as they sign, check it's a live person and keep a picture of their face beside their signature. Someone who can't use a camera signs with a code emailed to them instead.
        file_to_folio:
          type: boolean
          description: File the signed package in Folio; defaults to your Sig settings.
        check_with_nib:
          type: boolean
          description: Check each signature with Nib; defaults to your Sig settings.
        folio_folder:
          type: string
          description: The Folio folder to file it in, when your settings let senders choose; defaults to your Sig settings.
        folio_class:
          type: string
          description: The Folio retention class to file it under, when your settings let senders choose; defaults to your Sig settings.
        expires_in_days:
          type: integer
          minimum: 1
          maximum: 120
          default: 30
        return_url:
          type: string
          maxLength: 2000
          description: Where signers go once they've signed or declined, with `package_id`, `signer_id` and `outcome` added. It must start with `https://` and be on one of your verified domains or an origin in `PUT /sig/settings/return`. Defaults to your settings' `default_url`.
        send:
          type: boolean
          default: true
          description: false keeps it as a draft.
        sender:
          type: string
          maxLength: 254
          description: The email address of the person in your organisation it comes from. The signers' emails name them, and their replies go to them. Defaults to whoever made the API key.
        fields:
          type: array
          maxItems: 500
          description: Where each signer signs, initials and dates, if you'd rather say than have us find the places. Name the document and signer by their place in this request, from 0.
          items:
            $ref: "#/components/schemas/SigFieldRequest"
    SigSendRequest:
      type: object
      additionalProperties: false
      properties:
        sender:
          type: string
          maxLength: 254
          description: The email address of the person in your organisation it comes from. The signers' emails name them, and their replies go to them. Defaults to whoever made the API key.
    SigSignerKind:
      type: string
      description: "`signer` signs; `witness` watches one signer sign, then signs too; `approver` okays the package before the people after them get it, and signs nothing; `copy` gets the signed copy. An open set."
      enum:
        - signer
        - witness
        - approver
        - copy
    SigSignerRequest:
      type: object
      additionalProperties: false
      required:
        - name
        - email
      properties:
        kind:
          $ref: "#/components/schemas/SigSignerKind"
        name:
          type: string
          maxLength: 200
        email:
          type: string
          format: email
          description: A witness their signer names as they sign (`added_by_signer`) can leave it empty.
        witness_of:
          type: integer
          minimum: 0
          description: "For a witness: the place in the list of the signer they witness. They sign once that signer has."
        added_by_signer:
          type: boolean
          default: false
          description: "For a witness: their signer gives the name and email address once they have signed."
        mobile:
          type: string
        order:
          type: integer
          minimum: 1
          maximum: 20
          default: 1
        role:
          type: string
          maxLength: 60
          description: How the documents name this signer, such as Buyer or Tenant, so their places go to them.
        nib_reference:
          type: string
          maxLength: 128
          pattern: ^[A-Za-z0-9._-]+$
          description: "Where the package checks signatures with Nib: the reference of the person in Nib to compare this signer's signature with. Leave it out and we use the person named after their email address, keeping this signature as their first if they have none."
    SigDraftSigner:
      type: object
      additionalProperties: false
      properties:
        id:
          type: string
          description: Someone already on the draft.
        kind:
          $ref: "#/components/schemas/SigSignerKind"
        name:
          type: string
          maxLength: 200
        email:
          type: string
        mobile:
          type: string
        order:
          type: integer
          minimum: 1
          maximum: 20
          default: 1
        role:
          type: string
          maxLength: 60
        nib_reference:
          type: string
          maxLength: 128
        witness_of:
          type: integer
          minimum: 0
          description: "For a witness: the place in this list of the signer they witness."
        added_by_signer:
          type: boolean
          default: false
    SigDraftRequest:
      type: object
      additionalProperties: false
      properties:
        title:
          type: string
          maxLength: 200
        message:
          type: string
          maxLength: 2000
        reference:
          type: string
          maxLength: 200
        signers:
          type: array
          maxItems: 20
          items:
            $ref: "#/components/schemas/SigDraftSigner"
        fields:
          type: array
          maxItems: 500
          description: Name each field's document by its id (`document_id`) and its signer by their place in `signers` (`signer`).
          items:
            $ref: "#/components/schemas/SigFieldRequest"
        require_location:
          type: boolean
          default: false
        require_camera:
          type: boolean
          default: false
        file_to_folio:
          type: boolean
        check_with_nib:
          type: boolean
        folio_folder:
          type: string
        folio_class:
          type: string
        expires_in_days:
          type: integer
          minimum: 1
          maximum: 120
          default: 30
        send:
          type: boolean
          default: false
        return_url:
          type:
            - string
            - "null"
          maxLength: 2000
          description: Where signers go once they've finished; leave it out or null for your settings' default.
    SigSignerCorrection:
      type: object
      additionalProperties: false
      properties:
        name:
          type: string
          maxLength: 200
        email:
          type: string
          format: email
        mobile:
          type:
            - string
            - "null"
          description: null removes it.
    SigVoidRequest:
      type: object
      additionalProperties: false
      properties:
        reason:
          type: string
          maxLength: 500
    SigPackageStatus:
      type: string
      description: An open set; clients must accept values they don't know.
      enum:
        - draft
        - sent
        - partly_signed
        - completed
        - declined
        - voided
        - expired
    SigSignerStatus:
      type: string
      enum:
        - waiting
        - sent
        - viewed
        - signed
        - declined
    SigDocument:
      type: object
      required:
        - id
        - name
        - content_type
        - bytes
        - sha256
      properties:
        id:
          type: string
        name:
          type: string
        content_type:
          type: string
        bytes:
          type: integer
        sha256:
          type: string
        pages:
          type:
            - integer
            - "null"
        fields:
          type: string
          description: "found: we placed its fields; reading: a scan we are still reading; none: we found no places; given: you placed them. An open set."
          enum:
            - found
            - reading
            - none
            - given
        signed:
          description: The signed copy, once everyone has signed and it's ready.
          oneOf:
            - type: "null"
            - type: object
              required:
                - bytes
                - sha256
                - sealed
              properties:
                bytes:
                  type: integer
                sha256:
                  type: string
                sealed:
                  type: boolean
                  description: True when the copy carries our seal
                  so any later change to it shows.: null
    SigFieldKind:
      type: string
      description: An open set; clients must accept values they don't know.
      enum:
        - signature
        - initials
        - date
        - name
        - text
        - checkbox
        - choice
        - attachment
        - note
    SigField:
      type: object
      required:
        - id
        - document_id
        - kind
        - page
        - x
        - y
        - width
        - height
        - found
      properties:
        id:
          type: string
          example: sfd_01j9x3m8v7k2q4r5s6t7u8v9w0
        document_id:
          type: string
        signer_id:
          type:
            - string
            - "null"
          description: null when we couldn't tell whose it is.
        kind:
          $ref: "#/components/schemas/SigFieldKind"
        page:
          type: integer
          minimum: 1
        x:
          type: number
          minimum: 0
          maximum: 1
          description: From the page's left edge
          as a fraction of its width.: null
        y:
          type: number
          minimum: 0
          maximum: 1
          description: From the page's top edge
          as a fraction of its height.: null
        width:
          type: number
          minimum: 0
          maximum: 1
        height:
          type: number
          minimum: 0
          maximum: 1
        found:
          type: boolean
          description: True for a place we found and nobody has moved.
        label:
          type:
            - string
            - "null"
          description: What the signer is asked for, such as Account number; for a note, its words.
        required:
          type: boolean
          description: Whether the signer must fill it.
        options:
          type:
            - array
            - "null"
          items:
            type: string
          description: A choice's options.
        value:
          type:
            - string
            - "null"
          description: What the signer wrote or picked, once they have signed; `yes` for a ticked checkbox.
        attachment:
          description: The file the signer attached.
          oneOf:
            - type: "null"
            - type: object
              required:
                - name
                - content_type
                - bytes
                - sha256
              properties:
                name:
                  type: string
                content_type:
                  type: string
                bytes:
                  type: integer
                sha256:
                  type: string
    SigFieldRequest:
      type: object
      additionalProperties: false
      required:
        - kind
        - page
        - x
        - y
        - width
        - height
      properties:
        document:
          type: integer
          minimum: 0
          description: "When creating a package: the document's place in the request."
        document_id:
          type: string
          description: "When changing fields: the document's id."
        signer:
          type: integer
          minimum: 0
          description: "When creating a package: the signer's place in the request."
        signer_id:
          type: string
          description: "When changing fields: the signer's id."
        kind:
          $ref: "#/components/schemas/SigFieldKind"
        page:
          type: integer
          minimum: 1
        x:
          type: number
          minimum: 0
          maximum: 1
        y:
          type: number
          minimum: 0
          maximum: 1
        width:
          type: number
          minimum: 0.005
          maximum: 1
        height:
          type: number
          minimum: 0.005
          maximum: 1
        label:
          type: string
          maxLength: 1000
          description: What the signer is asked for (up to 200 characters); a note's words (up to 1000).
        required:
          type: boolean
          default: true
          description: Whether the signer must fill it. Signatures, initials and dates always are; notes never.
        options:
          type: array
          minItems: 2
          maxItems: 20
          items:
            type: string
            maxLength: 100
          description: A choice's options.
    SigFieldsRequest:
      type: object
      additionalProperties: false
      required:
        - fields
      properties:
        fields:
          type: array
          maxItems: 500
          items:
            $ref: "#/components/schemas/SigFieldRequest"
    SigSigner:
      type: object
      required:
        - id
        - kind
        - name
        - email
        - order
        - status
      properties:
        id:
          type: string
        kind:
          $ref: "#/components/schemas/SigSignerKind"
        witness_of:
          type:
            - string
            - "null"
          description: "For a witness: the signer they witness."
        added_by_signer:
          type: boolean
          description: "For a witness: their signer names them as they sign."
        name:
          type: string
        email:
          type: string
        mobile:
          type:
            - string
            - "null"
        role:
          type:
            - string
            - "null"
        order:
          type: integer
        status:
          $ref: "#/components/schemas/SigSignerStatus"
        sent_at:
          type:
            - string
            - "null"
          format: date-time
        viewed_at:
          type:
            - string
            - "null"
          format: date-time
        signed_at:
          type:
            - string
            - "null"
          format: date-time
        declined_at:
          type:
            - string
            - "null"
          format: date-time
        decline_reason:
          type:
            - string
            - "null"
        signed_with:
          type:
            - string
            - "null"
          enum:
            - drawn
            - photo
            - typed
            - null
          description: "How they signed: drawn on screen, a photo of a signature on paper, or their name typed."
        confirmed_with:
          type:
            - string
            - "null"
          enum:
            - camera
            - code
            - null
          description: "Where the package asked for the camera: whether they signed on camera or with a code emailed to them."
        camera:
          $ref: "#/components/schemas/SigCameraCheck"
        location:
          $ref: "#/components/schemas/SigLocation"
        nib_reference:
          type:
            - string
            - "null"
          description: The person in Nib chosen for this signer
          when one was.: null
        nib:
          $ref: "#/components/schemas/SigNibCheck"
        link:
          type: string
          description: The signer's link, only in the response that sent it.
        auto_reminders:
          type: integer
          description: How many reminders we have sent them on your behalf.
        next_reminder_at:
          type:
            - string
            - "null"
          format: date-time
          description: When we will next remind them, while they still have to sign.
        review:
          $ref: "#/components/schemas/SigReview"
    SigReview:
      type:
        - object
        - "null"
      description: When their signature looked different from the ones we hold, the review someone in your organisation does of it.
      required:
        - item_id
        - status
      properties:
        item_id:
          type: string
          description: The item to review in Desk.
        status:
          type: string
          enum:
            - waiting
            - done
        outcome:
          type:
            - string
            - "null"
          description: The outcome the reviewer chose, once done.
        accepted:
          type:
            - boolean
            - "null"
          description: Whether the reviewer accepted the signature, once done; null when the outcome does neither.
        decided_at:
          type:
            - string
            - "null"
          format: date-time
        decided_by:
          type:
            - string
            - "null"
    SigNibCheck:
      type:
        - object
        - "null"
      required:
        - outcome
        - tries
      properties:
        outcome:
          type: string
          enum:
            - matched
            - asked_again
            - doubtful
            - kept_as_reference
            - not_checked
          description: "`matched` the signatures Nib holds for them; `asked_again` after a first signature that looked different; `doubtful` when the second looked different too (a person reviews it in Desk); `kept_as_reference` when they had none yet; `not_checked` when it couldn't be compared."
        decision:
          type:
            - string
            - "null"
          enum:
            - AUTO_APPROVE
            - APPROVE
            - FLAG
            - FAILED
            - UNREADABLE
            - NO_SIGNATURE
            - NO_REFERENCE
            - ERROR
            - null
        probability:
          type:
            - number
            - "null"
        reference:
          type:
            - string
            - "null"
          description: The person in Nib it was compared with.
        job_id:
          type:
            - string
            - "null"
          description: The Nib check.
        tries:
          type: integer
          minimum: 1
          maximum: 2
        reason:
          type: string
        desk_item:
          type: string
          description: The Desk item a person reviews it in.
    SigCameraCheck:
      type:
        - object
        - "null"
      required:
        - result
      properties:
        result:
          type: string
          enum:
            - pending
            - live
            - not_live
            - no_face
          description: "`pending` while the video is checked, then `live`, `not_live` (it looked like a photo or a screen held up to the camera) or `no_face` (no face could be seen)."
        score:
          type:
            - number
            - "null"
          description: How sure the check is that it saw a live person, from 0 to 1.
    SigLocation:
      type:
        - object
        - "null"
      required:
        - latitude
        - longitude
      properties:
        latitude:
          type: number
          minimum: -90
          maximum: 90
        longitude:
          type: number
          minimum: -180
          maximum: 180
        accuracy:
          type:
            - number
            - "null"
          description: In metres
          as the signer's device reported it.: null
    SigEvent:
      type: object
      required:
        - id
        - kind
        - at
      properties:
        id:
          type: string
        kind:
          type: string
          description: created, sent, sent_to_signer, opened, reminded, corrected, fields_changed, code_sent, nib_doubtful, signed, approved, witness_named, nib_checked, camera, declined, completed, sealed, voided, expired. An open set.
        signer_id:
          type:
            - string
            - "null"
        actor:
          type:
            - string
            - "null"
        ip:
          type:
            - string
            - "null"
        user_agent:
          type:
            - string
            - "null"
        device:
          type:
            - string
            - "null"
          description: The browser and device, as a person would say it, e.g. "Safari 17 on an iPhone (iOS 17.5)".
        area:
          $ref: "#/components/schemas/SigArea"
        detail:
          type: object
        at:
          type: string
          format: date-time
    SigArea:
      type:
        - object
        - "null"
      description: Roughly where the IP address is. A city at best, and only ever approximate.
      required:
        - country
      properties:
        country:
          type: string
          description: ISO 3166 two-letter code.
        region:
          type: string
        city:
          type: string
    SigCertificate:
      type:
        - object
        - "null"
      required:
        - bytes
        - sha256
      properties:
        bytes:
          type: integer
        sha256:
          type: string
        time_stamped_at:
          type:
            - string
            - "null"
          format: date-time
          description: When a time-stamp authority dated the seal.
        check_code:
          type:
            - string
            - "null"
          description: The code anyone can enter at unforged.ai/v to check the certificate.
    SigCopyCheck:
      type: object
      required:
        - intact
        - sha256
      properties:
        intact:
          type: boolean
          description: Nothing in the file has changed since it was sealed.
        sealed_by:
          type:
            - string
            - "null"
        time_stamped_at:
          type:
            - string
            - "null"
          format: date-time
        sha256:
          type: string
        found:
          type:
            - object
            - "null"
          description: The package this exact file came from, when it's one of yours.
          required:
            - package_id
            - title
            - kind
          properties:
            package_id:
              type: string
            title:
              type: string
            kind:
              type: string
              enum:
                - signed_copy
                - certificate
            document_id:
              type:
                - string
                - "null"
            document_name:
              type:
                - string
                - "null"
            sealed_at:
              type:
                - string
                - "null"
              format: date-time
    SigPackage:
      type: object
      required:
        - id
        - title
        - status
        - expires_at
        - created_at
        - documents
        - signers
        - fields
      properties:
        id:
          type: string
          example: pkg_01j9x3m8v7k2q4r5s6t7u8v9w0
        title:
          type: string
        message:
          type:
            - string
            - "null"
        reference:
          type:
            - string
            - "null"
        status:
          $ref: "#/components/schemas/SigPackageStatus"
        require_location:
          type: boolean
        require_camera:
          type: boolean
        file_to_folio:
          type: boolean
        check_with_nib:
          type: boolean
        folio:
          $ref: "#/components/schemas/SigFolioFiling"
        expires_at:
          type: string
          format: date-time
        created_by:
          type: string
        created_at:
          type: string
          format: date-time
        sent_at:
          type:
            - string
            - "null"
          format: date-time
        completed_at:
          type:
            - string
            - "null"
          format: date-time
        sealed_at:
          type:
            - string
            - "null"
          format: date-time
          description: When the signed copies and certificate were ready.
        voided_at:
          type:
            - string
            - "null"
          format: date-time
        void_reason:
          type:
            - string
            - "null"
        return_url:
          type:
            - string
            - "null"
          description: Where its signers go once they've finished, if it has its own link.
        certificate:
          $ref: "#/components/schemas/SigCertificate"
        documents:
          type: array
          items:
            $ref: "#/components/schemas/SigDocument"
        signers:
          type: array
          items:
            $ref: "#/components/schemas/SigSigner"
        fields:
          type: array
          items:
            $ref: "#/components/schemas/SigField"
        events:
          type: array
          items:
            $ref: "#/components/schemas/SigEvent"
    SigFolioFiling:
      type: object
      description: Where the package is filed in Folio, when it is to be.
      required:
        - folder
        - class
        - path
        - filed_at
        - records
      properties:
        folder:
          type: string
          description: The folder chosen when it was sent.
        class:
          type:
            - string
            - "null"
          description: The retention class; null is Folio's default.
        path:
          type:
            - string
            - "null"
          description: The package's own folder
          once filed.: null
        filed_at:
          type:
            - string
            - "null"
          format: date-time
        records:
          type: array
          items:
            type: object
            required:
              - part
              - name
              - record_id
            properties:
              part:
                type: string
                description: "`document:<id>`, `certificate`, `still:<signer>` or `clip:<signer>`."
              name:
                type: string
              record_id:
                type: string
    SigPackageSummary:
      type: object
      required:
        - id
        - title
        - status
        - signers
        - signed
        - expires_at
        - created_at
      properties:
        id:
          type: string
        title:
          type: string
        reference:
          type:
            - string
            - "null"
        status:
          $ref: "#/components/schemas/SigPackageStatus"
        signers:
          type: integer
        signed:
          type: integer
        expires_at:
          type: string
          format: date-time
        created_by:
          type: string
        created_at:
          type: string
          format: date-time
        sent_at:
          type:
            - string
            - "null"
          format: date-time
        completed_at:
          type:
            - string
            - "null"
          format: date-time
        reviews_waiting:
          type: integer
          description: How many of its signatures are waiting for someone to review them.
        people:
          type: array
          items:
            type: string
          description: Its signers, in the order they sign.
        waiting_on:
          type: array
          items:
            type: string
          description: The signers who have it now and have still to sign.
        last_activity_at:
          type: string
          format: date-time
          description: When anything last happened on it.
    SigCounts:
      type: object
      required:
        - with_signers
        - reviews_waiting
        - drafts
        - signed_this_month
      properties:
        with_signers:
          type: integer
          description: Packages sent and still waiting on someone to sign.
        reviews_waiting:
          type: integer
          description: Signatures waiting for someone to review them.
        drafts:
          type: integer
        signed_this_month:
          type: integer
          description: Packages everyone signed this UTC month.
    SigPackagePage:
      type: object
      required:
        - packages
      properties:
        packages:
          type: array
          items:
            $ref: "#/components/schemas/SigPackageSummary"
        next_cursor:
          type:
            - string
            - "null"
    SigWebhook:
      type: object
      required:
        - id
        - url
        - events
        - created_at
      properties:
        id:
          type: string
        url:
          type: string
          format: uri
        events:
          type: array
          items:
            type: string
        created_at:
          type: string
          format: date-time
    SigWebhookCreated:
      allOf:
        - $ref: "#/components/schemas/SigWebhook"
        - type: object
          required:
            - secret
          properties:
            secret:
              type: string
              description: Shown once.
    SigWebhookList:
      type: object
      required:
        - webhooks
      properties:
        webhooks:
          type: array
          items:
            $ref: "#/components/schemas/SigWebhook"
    SigWebhookCreate:
      type: object
      additionalProperties: false
      required:
        - url
        - events
      properties:
        url:
          type: string
          format: uri
        events:
          type: array
          minItems: 1
          items:
            type: string
            enum:
              - sig.package.sent
              - sig.signer.viewed
              - sig.signer.signed
              - sig.package.completed
              - sig.package.declined
              - sig.package.voided
              - sig.package.expired
    SigReturnSettings:
      type: object
      required:
        - default_url
        - origins
        - updated_at
      properties:
        default_url:
          type:
            - string
            - "null"
          description: Where signers go once they've finished, unless a package has its own link.
        origins:
          type: array
          items:
            type: string
          description: Origins a return link may use besides your verified domains, such as `https://portal.example.com`.
        updated_at:
          type:
            - string
            - "null"
          format: date-time
    SigReturnRequest:
      type: object
      additionalProperties: false
      properties:
        default_url:
          type:
            - string
            - "null"
          maxLength: 2000
        origins:
          type: array
          maxItems: 20
          items:
            type: string
    SigSettings:
      type: object
      additionalProperties: false
      required:
        - file_to_folio
        - check_with_nib
        - folio_folder
        - folio_class
        - folio_sender_chooses
        - replies_to_org
        - attach_copy
        - updated_at
      properties:
        file_to_folio:
          type: boolean
          description: File each completed package, its certificate and its evidence in Folio. Needs Folio.
        check_with_nib:
          type: boolean
          description: Check each signature with Nib against the signer's references as it's made.
        folio_folder:
          type: string
          description: The Folio folder packages are filed in.
        folio_class:
          type:
            - string
            - "null"
          description: The retention class they're filed under; null is Folio's default.
        folio_sender_chooses:
          type: boolean
          description: Senders may choose the folder and class for each package.
        replies_to_org:
          type: boolean
          description: Signers' replies go to your organisation's reply-to address rather than to the person who sent the package.
        attach_copy:
          type: boolean
          description: The signed copy is attached to the email everyone gets once the package is complete. Off sends a link instead.
        updated_at:
          type:
            - string
            - "null"
          format: date-time
          description: When the settings were last changed; null when they never have been.
      example:
        file_to_folio: true
        check_with_nib: false
        folio_folder: Signed
        folio_class: null
        folio_sender_chooses: false
        replies_to_org: false
        attach_copy: true
        updated_at: 2026-10-13T10:00:00Z
    SigUsage:
      type: object
      additionalProperties: false
      required:
        - period_start
        - period_end
        - packages_sent
        - packages_completed
        - documents_signed
        - unit_price
        - estimated_charge
        - currency
        - pricing
      properties:
        period_start:
          type: string
          format: date-time
        period_end:
          type: string
          format: date-time
        packages_sent:
          type: integer
          minimum: 0
        packages_completed:
          type: integer
          minimum: 0
        documents_signed:
          type: integer
          minimum: 0
          description: Documents in packages everyone signed this month. These are what's charged.
        unit_price:
          type:
            - string
            - "null"
          description: The price of each signed document, as a decimal; null when your contract covers Sig for a fixed fee.
        estimated_charge:
          type: string
          description: The charge so far this month
          as a decimal.: null
        currency:
          type: string
          enum:
            - usd
            - gbp
            - eur
        pricing:
          type: string
          enum:
            - list
            - contract
          description: "`list` is the published price; `contract` is the price agreed in your contract."
      example:
        period_start: 2026-10-01T00:00:00Z
        period_end: 2026-11-01T00:00:00Z
        packages_sent: 42
        packages_completed: 37
        documents_signed: 61
        unit_price: "0.65"
        estimated_charge: "39.65"
        currency: gbp
        pricing: list
    SigSettingsRequest:
      type: object
      additionalProperties: false
      required:
        - file_to_folio
        - check_with_nib
      properties:
        file_to_folio:
          type: boolean
        check_with_nib:
          type: boolean
        folio_folder:
          type: string
          description: Unchanged when left out.
        folio_class:
          type: string
          description: Empty for Folio's default; unchanged when left out.
        folio_sender_chooses:
          type: boolean
          description: Unchanged when left out.
        replies_to_org:
          type: boolean
          description: Unchanged when left out.
        attach_copy:
          type: boolean
          description: Unchanged when left out.
    Problem:
      type: object
      description: An RFC 9457 problem document.
      required:
        - type
        - title
        - status
        - detail
        - code
      properties:
        type:
          type: string
          description: "`https://unforged.sh/problems/<code>`"
        title:
          type: string
        status:
          type: integer
        detail:
          type: string
        code:
          type: string
          enum:
            - invalid_request
            - unsupported_media
            - too_large
            - fetch_failed
            - rate_limited
            - quota_exceeded
            - idempotency_conflict
            - internal
            - not_found
            - unauthorized
            - permission_denied
            - timeout
            - not_available_in_dev
            - method_not_allowed
            - conflict
            - busy
            - not_ready
            - idempotency_in_progress
            - idempotency_key_required
            - idempotency_not_replayable
            - not_implemented
            - not_allowlisted
            - tenant_not_provisioned
            - billing_required
            - invalid_signature
            - billing_already_active
            - invalid_version
            - org_suspended
            - wrong_region
            - org_moving
            - learning_terms_required
            - webhook_test_failed
            - awaiting_first_payment
            - reason_required
            - access_suspended
            - storage_provisioning
            - address_not_allowed
          description: A stable error code.
        request_id:
          type: string
        region:
          type: string
          description: On `wrong_region`, your home region.
        api_host:
          type: string
          description: On `wrong_region`, that region's API host.
        address:
          type: string
          description: On `address_not_allowed`, the address your request came from.
        terms_version:
          type: string
          description: On `learning_terms_required`, the version to accept.
        job_id:
          type: string
          description: On `busy` or `timeout` of a check that continues.
        status_url:
          type: string
        host:
          type: string
          description: On a failed URL fetch, the host.
        reason:
          type: string
          description: On a failed URL fetch, why (e.g. `http_status`, `timeout`). On `org_suspended`, `idle` when the organisation was removed for inactivity (it can start again).
        checks:
          type: object
          description: On `not_ready`.
        errors:
          type: array
          description: On a synchronous check whose job failed.
      examples:
        - type: https://unforged.sh/problems/not_found
          title: Not found
          status: 404
          detail: job not found
          code: not_found
          request_id: req_01j9x3m8v7k2q4r5s6t7u8v9w0
        - type: https://unforged.sh/problems/timeout
          title: Request timed out
          status: 504
          detail: the item exceeded its 30 s budget; the check continues asynchronously
          code: timeout
          request_id: req_01j9x3m8v7k2q4r5s6t7u8v9w0
          job_id: inv-2291
          status_url: /nib/checks/inv-2291
