{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://artifacts.apis.io/inkseal-net/json-schema/inkseal-net-envelope-detail-schema.json",
  "title": "EnvelopeDetail",
  "description": "One envelope in full, as `GET /envelopes/{envelopeId}` publishes it.",
  "x-generated": "2026-11-12",
  "x-method": "derived",
  "x-generator": "derive-json-schema.py",
  "x-source": "openapi/inkseal-net-openapi.json#/components/schemas/EnvelopeDetail",
  "type": "object",
  "required": [
    "id",
    "status",
    "subject",
    "message",
    "signingOrder",
    "emailVerification",
    "template",
    "document",
    "signers",
    "readiness",
    "values",
    "completedPdfHash",
    "sentAt",
    "completedAt",
    "expiresAt",
    "createdAt",
    "pendingApproval",
    "activeStep",
    "steps",
    "creditCost",
    "creditLines",
    "attachments",
    "publicLinkId"
  ],
  "properties": {
    "id": {
      "type": "string"
    },
    "status": {
      "$ref": "#/$defs/EnvelopeStatus"
    },
    "subject": {
      "type": [
        "string",
        "null"
      ]
    },
    "message": {
      "type": [
        "string",
        "null"
      ],
      "description": "Body text carried in the invitation email."
    },
    "signingOrder": {
      "type": "string",
      "enum": [
        "parallel",
        "sequential"
      ]
    },
    "emailVerification": {
      "type": "boolean",
      "description": "Whether a signer must pass an emailed one-time code before signing."
    },
    "template": {
      "$ref": "#/$defs/EnvelopeTemplateProvenance"
    },
    "document": {
      "$ref": "#/$defs/EnvelopeDocumentSummary"
    },
    "signers": {
      "type": "array",
      "items": {
        "$ref": "#/$defs/SignerDetail"
      }
    },
    "readiness": {
      "$ref": "#/$defs/EnvelopeReadiness"
    },
    "values": {
      "$ref": "#/$defs/NamedFieldValues"
    },
    "completedPdfHash": {
      "type": [
        "string",
        "null"
      ],
      "description": "SHA-256 of the executed PDF. Null until the envelope completes."
    },
    "sentAt": {
      "type": [
        "string",
        "null"
      ],
      "format": "date-time"
    },
    "completedAt": {
      "type": [
        "string",
        "null"
      ],
      "format": "date-time"
    },
    "expiresAt": {
      "type": [
        "string",
        "null"
      ],
      "format": "date-time"
    },
    "createdAt": {
      "type": "string",
      "format": "date-time"
    },
    "pendingApproval": {
      "type": [
        "object",
        "null"
      ],
      "description": "The approval a person still has to give before this draft is sent, on a workspace whose `agentSendApproval` setting is `required`. Null on every other envelope. This is what an agent polls after `sendEnvelope` answers `pending_approval`: the block clears and the status leaves `draft` once a person approves.",
      "required": [
        "id",
        "requestedBy",
        "requestedAt",
        "expiresAt"
      ],
      "additionalProperties": false,
      "properties": {
        "id": {
          "type": "string",
          "description": "The approval's public id, as returned by `sendEnvelope`."
        },
        "requestedBy": {
          "type": [
            "string",
            "null"
          ],
          "description": "The agent identity of the credential that asked."
        },
        "requestedAt": {
          "type": "string",
          "format": "date-time"
        },
        "expiresAt": {
          "type": "string",
          "format": "date-time",
          "description": "When the request lapses if nobody answers it."
        }
      }
    },
    "activeStep": {
      "type": [
        "integer",
        "null"
      ],
      "description": "The step whose signers may act now, or null once every signer has signed."
    },
    "steps": {
      "type": "array",
      "description": "The routing chain, however the envelope was created.",
      "items": {
        "$ref": "#/$defs/EnvelopeSigningStep"
      }
    },
    "creditCost": {
      "type": "integer",
      "description": "Credits sending this envelope costs: one for the envelope, one for each signer who is not the sender, and one more when `emailVerification` is on. An unmetered workspace is quoted the same price and charged nothing."
    },
    "creditLines": {
      "type": "array",
      "description": "The same total broken into its lines.",
      "items": {
        "$ref": "#/$defs/CreditLine"
      }
    },
    "attachments": {
      "type": "array",
      "items": {
        "$ref": "#/$defs/EnvelopeAttachment"
      },
      "description": "Unsigned files carried by signing request emails, in caller order."
    },
    "publicLinkId": {
      "type": [
        "string",
        "null"
      ],
      "description": "The originating public link id, or null."
    }
  },
  "$defs": {
    "CreditLine": {
      "type": "object",
      "description": "One line of what an envelope costs to send. The envelope line is always present; a signer line appears when the envelope has signers the workspace pays for, and an `otp` line when it asks for a one-time code.",
      "required": [
        "kind",
        "quantity",
        "unitCredits",
        "credits"
      ],
      "properties": {
        "kind": {
          "type": "string",
          "enum": [
            "envelope",
            "signer",
            "otp"
          ]
        },
        "quantity": {
          "type": "integer",
          "description": "How many units this line charges for."
        },
        "unitCredits": {
          "type": "integer",
          "description": "Credits per unit."
        },
        "credits": {
          "type": "integer",
          "description": "`quantity` times `unitCredits`."
        }
      }
    },
    "EnvelopeAttachment": {
      "type": "object",
      "required": [
        "id",
        "filename",
        "contentType",
        "sizeBytes"
      ],
      "properties": {
        "id": {
          "type": "string"
        },
        "filename": {
          "type": "string"
        },
        "contentType": {
          "type": "string"
        },
        "sizeBytes": {
          "type": "integer"
        }
      }
    },
    "EnvelopeDocumentSummary": {
      "type": "object",
      "description": "The document the envelope was built from, as the envelope routes publish it: the public id and the original upload filename, nothing else.",
      "required": [
        "id",
        "filename"
      ],
      "properties": {
        "id": {
          "type": "string",
          "description": "Public document id."
        },
        "filename": {
          "type": "string",
          "description": "The filename as uploaded."
        }
      }
    },
    "EnvelopeReadiness": {
      "type": "object",
      "description": "Computed send readiness. It is never persisted and may change after a draft edit.",
      "required": [
        "canSend",
        "issues"
      ],
      "properties": {
        "canSend": {
          "type": "boolean",
          "description": "True when no readiness issue blocks sending. Credit availability is separate."
        },
        "issues": {
          "type": "array",
          "items": {
            "$ref": "#/$defs/ReadinessIssue"
          }
        }
      }
    },
    "EnvelopeSigningStep": {
      "type": "object",
      "additionalProperties": false,
      "description": "One step of the routing chain. Everyone in it is emailed together.",
      "required": [
        "step",
        "signers"
      ],
      "properties": {
        "step": {
          "type": "integer",
          "minimum": 1
        },
        "status": {
          "type": "string",
          "enum": [
            "completed",
            "active",
            "pending"
          ],
          "description": "Where this step sits relative to the step the envelope is on."
        },
        "signers": {
          "type": "array",
          "items": {
            "type": "string"
          },
          "description": "Public ids of the signers in this step."
        }
      }
    },
    "EnvelopeStatus": {
      "type": "string",
      "description": "Where the envelope is in its lifecycle. `sending` and `completing` are transient; `completed`, `declined`, `voided`, and `expired` are terminal.",
      "enum": [
        "draft",
        "sending",
        "pending",
        "completing",
        "completed",
        "declined",
        "voided",
        "expired"
      ]
    },
    "EnvelopeTemplateProvenance": {
      "type": [
        "object",
        "null"
      ],
      "required": [
        "id",
        "version"
      ],
      "properties": {
        "id": {
          "type": [
            "string",
            "null"
          ],
          "description": "Null after the source template is deleted."
        },
        "version": {
          "type": "integer",
          "minimum": 1
        }
      }
    },
    "FieldDetail": {
      "type": "object",
      "description": "A field with its fill state, as `GET /envelopes/{envelopeId}` publishes it. The stored value is never returned; only whether and when it was filled.",
      "required": [
        "id",
        "type",
        "required",
        "pageIndex",
        "filled",
        "filledAt"
      ],
      "properties": {
        "id": {
          "type": "string"
        },
        "type": {
          "$ref": "#/$defs/FieldType"
        },
        "required": {
          "type": "boolean",
          "description": "Whether the signer must fill this field to complete."
        },
        "pageIndex": {
          "type": "integer",
          "minimum": 0
        },
        "filled": {
          "type": "boolean",
          "description": "True once the field carries a value."
        },
        "filledAt": {
          "type": [
            "string",
            "null"
          ],
          "format": "date-time"
        }
      }
    },
    "FieldType": {
      "type": "string",
      "description": "How the field is filled and rendered. Values are stored as strings; signature and initials use images.",
      "enum": [
        "signature",
        "initials",
        "text",
        "date",
        "checkbox",
        "number",
        "email",
        "select",
        "radio",
        "textarea"
      ]
    },
    "InvalidSenderValueIssue": {
      "type": "object",
      "required": [
        "code",
        "fieldId",
        "name",
        "detail"
      ],
      "properties": {
        "code": {
          "type": "string",
          "enum": [
            "invalid_sender_value"
          ]
        },
        "fieldId": {
          "type": "string"
        },
        "name": {
          "type": [
            "string",
            "null"
          ]
        },
        "detail": {
          "type": "string",
          "description": "Safe, human-readable validation detail."
        }
      }
    },
    "MissingSenderValueIssue": {
      "type": "object",
      "required": [
        "code",
        "fieldId",
        "name",
        "label"
      ],
      "properties": {
        "code": {
          "type": "string",
          "enum": [
            "missing_sender_value"
          ]
        },
        "fieldId": {
          "type": "string"
        },
        "name": {
          "type": [
            "string",
            "null"
          ]
        },
        "label": {
          "type": [
            "string",
            "null"
          ]
        }
      }
    },
    "NamedFieldValues": {
      "type": "object",
      "description": "Named field values keyed by the stable field name. Unnamed fields are omitted. Values keep their stored string representation; an unfilled field is null.",
      "additionalProperties": {
        "type": [
          "string",
          "null"
        ]
      }
    },
    "NoSignerFieldsIssue": {
      "type": "object",
      "required": [
        "code"
      ],
      "properties": {
        "code": {
          "type": "string",
          "enum": [
            "no_signer_fields"
          ]
        }
      }
    },
    "ReadinessIssue": {
      "oneOf": [
        {
          "$ref": "#/$defs/MissingSenderValueIssue"
        },
        {
          "$ref": "#/$defs/InvalidSenderValueIssue"
        },
        {
          "$ref": "#/$defs/UnassignedFieldIssue"
        },
        {
          "$ref": "#/$defs/NoSignerFieldsIssue"
        }
      ]
    },
    "SignerDetail": {
      "type": "object",
      "description": "A signer with routing position, lifecycle timestamps, and the fields assigned to them.",
      "required": [
        "id",
        "name",
        "email",
        "role",
        "status",
        "order",
        "viewedAt",
        "signedAt",
        "declinedAt",
        "declineReason",
        "fields",
        "identityVerification",
        "identityVerifiedAt",
        "roleKey"
      ],
      "properties": {
        "id": {
          "type": "string"
        },
        "name": {
          "type": "string"
        },
        "email": {
          "type": "string",
          "format": "email"
        },
        "role": {
          "$ref": "#/$defs/SignerRole"
        },
        "status": {
          "$ref": "#/$defs/SignerStatus"
        },
        "order": {
          "type": "integer",
          "minimum": 1,
          "description": "Normalized 1-based routing position. On a parallel envelope it is still assigned but does not gate anything."
        },
        "identityVerification": {
          "type": "boolean",
          "description": "Whether the sender required this signer to verify a government ID through Stripe Identity."
        },
        "identityVerifiedAt": {
          "type": [
            "string",
            "null"
          ],
          "format": "date-time",
          "description": "When Stripe Identity confirmed the signer's ID. Null until then, and always null when no ID was required."
        },
        "viewedAt": {
          "type": [
            "string",
            "null"
          ],
          "format": "date-time"
        },
        "signedAt": {
          "type": [
            "string",
            "null"
          ],
          "format": "date-time"
        },
        "declinedAt": {
          "type": [
            "string",
            "null"
          ],
          "format": "date-time"
        },
        "declineReason": {
          "type": [
            "string",
            "null"
          ],
          "description": "What the signer typed when declining, when they gave a reason."
        },
        "fields": {
          "type": "array",
          "items": {
            "$ref": "#/$defs/FieldDetail"
          }
        },
        "roleKey": {
          "type": [
            "string",
            "null"
          ],
          "pattern": "^[a-z][a-z0-9_]{0,31}$",
          "description": "Key of the template role this signer was created for, such as `client`. Null when the envelope was not created from a template or public link, and for signers added to it afterwards. Distinct from `role`, which is what the recipient is asked to do."
        }
      }
    },
    "SignerRole": {
      "type": "string",
      "description": "What the recipient is asked to do. `signer` executes the document and must be assigned at least one field. `approver` approves or declines and may hold fields. `reviewer` acknowledges having read it and may hold none. Defaults to `signer`.",
      "enum": [
        "signer",
        "approver",
        "reviewer"
      ]
    },
    "SignerStatus": {
      "type": "string",
      "description": "Where one recipient is. `pending` means not yet invited, which on a sequential envelope is normal for everyone behind the current turn. `signed`, `approved` and `acknowledged` are the three positive terminal states, one per role: a signer signs, an approver approves, a reviewer acknowledges.",
      "enum": [
        "pending",
        "sent",
        "viewed",
        "signed",
        "approved",
        "acknowledged",
        "declined"
      ]
    },
    "UnassignedFieldIssue": {
      "type": "object",
      "description": "Reserved for forward compatibility. Current persisted envelopes do not produce this issue.",
      "required": [
        "code",
        "fieldId"
      ],
      "properties": {
        "code": {
          "type": "string",
          "enum": [
            "unassigned_field"
          ]
        },
        "fieldId": {
          "type": "string"
        }
      }
    }
  }
}