---
title: "Get an envelope's audit trail"
method: GET
path: "/envelopes/{envelopeId}/audit-trail"
tags: ["Envelopes"]
---

# Get an envelope's audit trail

`GET /envelopes/{envelopeId}/audit-trail`

## Request

- Base URL: `https://inkseal.net/api/v1`
- URL: `https://inkseal.net/api/v1/envelopes/{envelopeId}/audit-trail`
- Auth: HTTP bearer

The signing evidence for one envelope, oldest first, one page at a time. `page` is 1-based, `limit` defaults to 20 and is capped at 100, and the response carries the standard `pagination` block. Rows with the same timestamp are ordered by their internal sequence, so walking a stable trail does not repeat or skip an entry.

Because the trail names every signer and everything they did, it costs both `envelopes:read` and `account:read`. Either grant alone is weaker than the answer.

Actions taken through a service credential are recorded with `actorType: "service"` and `actorName` set to the credential's agent identity; signer activity is `actorType: "signer"`; dashboard activity is `user`; scheduled jobs are `system`. `metadata` is the parsed JSON the action recorded, or null.

Four events are recorded about a recipient rather than by an actor: `signer.notified`, `signer.reminder_sent`, `envelope.cc_delivered` and `envelope.cc_delivery_failed`. Those rows carry the person written to as `metadata.recipientName` and `metadata.recipientEmail`, and their actor fields are null unless a person triggered the send, in which case the actor is that person. Never read a recipient out of `actorEmail`.

## Query parameters

- `page` integer
- `limit` integer

## Response `200`

The envelope's audit entries, oldest first.

- object
  - `data` AuditTrailEntry[], required
    - `action` 'envelope.created' | 'envelope.sent' | 'envelope.completed' | 'envelope.declined' | 'envelope.voided' | 'envelope.expired' | 'envelope.send_failed' | 'signer.notified' | 'signer.viewed' | 'signer.signed' | 'signer.approved' | 'signer.acknowledged' | 'signer.declined' | 'signer.reminder_sent' | 'document.uploaded' | 'document.converted' | 'document.stamped' | 'document.downloaded' | 'email.received' | 'email.parsed' | 'email.needs_info' | 'email.preview_sent' | 'email.approved' | 'email.cancelled' | 'email.rejected' | 'email.sender_auth' | 'account.email_keyword_approval_updated' | 'envelope.send_approval_requested' | 'envelope.send_approval_decided' | 'account.agent_guardrails_updated', required: What happened. Envelope lifecycle, signer activity, document handling, and the inbound-mail workflow all record here.
    - `actorType` 'user' | 'signer' | 'system' | 'service', required: Who acted. `service` is a service credential, `user` a dashboard member, `signer` a recipient, `system` a scheduled job.
    - `actorEmail` string, nullable, required: The email of whoever caused the event. For a credential this is the issuing member, which is attribution only. Null on an event recorded about a recipient rather than by an actor: read `metadata.recipientEmail` for those.
    - `actorName` string, nullable, required: Display name of whoever caused the event. For a credential this is its agent identity. Null on an event recorded about a recipient: read `metadata.recipientName` for those.
    - `ipAddress` string, nullable, required: The address the action came from, when one was recorded.
    - `metadata` object, nullable, required: Action-specific detail, parsed from storage. Null when the action recorded none. The four events recorded about a recipient rather than by an actor -- `signer.notified`, `signer.reminder_sent`, `envelope.cc_delivered` and `envelope.cc_delivery_failed` -- carry that person here as `recipientName` and `recipientEmail`.
    - `createdAt` string, date-time, required
    - `credential` object, nullable, required: The service credential that produced this event, when one did. Its public id and name only: no key material and no prefix.
      - `id` string, required: The credential's public id, stable across rotations.
      - `name` string, required
    - `agentIdentity` string, nullable, required: Who the credential acts as, as recorded when the event happened.
  - `pagination` Pagination, required: Where the returned page sits in the full result set.
    - `page` integer, required: The 1-based page returned.
    - `limit` integer, required: Items per page, as requested.
    - `total` integer, required: Total items matching the query.
    - `totalPages` integer, required: `ceil(total / limit)`.

## Other responses

- `400`: The request is malformed or the resource is in the wrong state for a synchronous check. Codes: `validation_error`, `invalid_body`, `invalid_parameter`, `invalid_idempotency_key`, `invalid_event_id`, `invalid_file`, `invalid_url`, `invalid_status`, `key_mismatch`, `no_signers`, `not_completed`, `envelope_exists`, `document_not_ready`, `upload_failed`.
- `401`: The credential is missing, malformed, unknown, expired, revoked, or past its rotation grace window. Code: `unauthorized`. Every authentication failure is deliberately indistinguishable.
- `403`: The credential is valid but may not do this. Codes: `insufficient_scope` (the route's scopes are not all held; `details` names `requiredScopes` and `missingScopes`), `forbidden` (the capability check on the resolved resource failed), `scope_escalation` (a grant wider than the caller's own was requested).
- `404`: The resource does not exist, was deleted, or belongs to another workspace. Code: `not_found`. The three cases are answered identically on purpose, so a 404 never confirms that an identifier exists elsewhere.
- `429`: A limit was reached. Codes: `rate_limited` (the credential's per-bucket HTTP limit, or the per-workspace detection throttle), `workspace_limit_exceeded` (a workspace content or send limit; `details.limit` names it), `daily_budget_exceeded` (the workspace's daily detection budget). The four rate-limit headers are sent on this response and on no successful one, so a client cannot read its remaining budget without being refused. Retrying after `Retry-After` is always safe: a rate-limit refusal happens before the handler runs.
- `503`: A dependency the request needs is unreachable and nothing was changed. Codes: `queue_unavailable` (a background job could not be enqueued; retry the call), `billing_unavailable` (Stripe would not open a checkout session; nothing was charged, retry the call), `service_unavailable` (the rate-limit store is down, so the request was refused before running).

---

[API](https://skmtc.dev/inkseal/apis/inkseal-api.md) · [All operations](https://skmtc.dev/inkseal/apis/inkseal-api/llms.txt) · [OpenAPI document](https://skmtc.dev/inkseal/apis/inkseal-api/revisions/c440c981d578?raw)
