{
  "openapi": "3.1.0",
  "info": {
    "title": "Termitude Public API",
    "version": "1.0.0",
    "description": "Public REST API for Termitude. Authenticate every request with a workspace API key (generated under Settings → API keys). Send the key as `Authorization: Bearer tmt_…` or `x-api-key: tmt_…`. All endpoints are CORS-enabled.\n\n## Webhooks\n\nTermitude posts signed JSON to endpoints you configure under Settings → Webhooks. Every request carries:\n\n- `x-termitude-event` — event type (e.g. `version.published`).\n- `x-termitude-delivery` — stable delivery id. Retries reuse the same value; dedupe on it.\n- `x-termitude-timestamp` — unix seconds when the signature was generated.\n- `x-termitude-signature` — `t=<timestamp>,v1=<hex_sha256>`.\n\nVerify by computing `HMAC_SHA256(endpoint_secret, \"{timestamp}.{rawBody}\")` and timing-safe comparing the hex digest against the `v1` value. Use the raw request body (do not re-stringify the JSON) and reject requests where `|now − timestamp| > 300` seconds to prevent replay. Copy-pasteable verifiers for Node, Python, Go, Ruby, and PHP are in the [developer docs](/docs#webhooks)."
  },
  "servers": [
    {
      "url": "https://app.termitude.com"
    }
  ],
  "components": {
    "securitySchemes": {
      "ApiKeyAuth": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "tmt_..."
      },
      "XApiKey": {
        "type": "apiKey",
        "in": "header",
        "name": "x-api-key"
      }
    },
    "schemas": {
      "Error": {
        "type": "object",
        "properties": {
          "error": {
            "type": "string"
          },
          "detail": {
            "type": "string"
          }
        },
        "required": [
          "error"
        ]
      },
      "Document": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "name": {
            "type": "string"
          },
          "slug": {
            "type": "string"
          },
          "category": {
            "type": "string",
            "description": "One of: terms_of_service, privacy_policy, dpa, cookie_policy, acceptable_use, ai_usage, sla, security_policy, addendum, custom."
          },
          "status": {
            "type": "string",
            "enum": [
              "draft",
              "in_review",
              "published",
              "archived"
            ]
          },
          "effective_date": {
            "type": "string",
            "format": "date",
            "nullable": true
          },
          "current_version_id": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          }
        }
      },
      "DocumentVersion": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "version_number": {
            "type": "string"
          },
          "content_html": {
            "type": "string"
          },
          "content_hash": {
            "type": "string",
            "nullable": true
          },
          "classification": {
            "type": "string",
            "enum": [
              "major",
              "minor",
              "unclassified"
            ]
          },
          "effective_date": {
            "type": "string",
            "format": "date",
            "nullable": true
          },
          "published_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "ai_summary": {
            "type": "string",
            "nullable": true
          }
        }
      },
      "ConsentInput": {
        "type": "object",
        "required": [
          "documentSlug"
        ],
        "properties": {
          "documentSlug": {
            "type": "string"
          },
          "userEmail": {
            "type": "string",
            "format": "email"
          },
          "userExternalId": {
            "type": "string"
          },
          "customerId": {
            "type": "string"
          },
          "accountId": {
            "type": "string"
          },
          "method": {
            "type": "string",
            "enum": [
              "checkbox",
              "click",
              "login",
              "forced_reaccept",
              "account_level"
            ],
            "default": "click"
          },
          "sourceApp": {
            "type": "string"
          },
          "country": {
            "type": "string"
          },
          "acceptedText": {
            "type": "string",
            "description": "Exact text shown to the user. Hashed server-side for evidence."
          },
          "versionNumber": {
            "type": "string",
            "description": "Pin to a specific version. Defaults to the current published version."
          }
        }
      },
      "ConsentResult": {
        "type": "object",
        "properties": {
          "ok": {
            "type": "boolean"
          },
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "accepted_at": {
            "type": "string",
            "format": "date-time"
          },
          "version_id": {
            "type": "string",
            "format": "uuid"
          }
        }
      },
      "ConsentStatusItem": {
        "type": "object",
        "properties": {
          "document_id": {
            "type": "string",
            "format": "uuid"
          },
          "slug": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "category": {
            "type": "string"
          },
          "current_version_id": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "current_version_number": {
            "type": "string",
            "nullable": true
          },
          "current_effective_date": {
            "type": "string",
            "format": "date",
            "nullable": true
          },
          "accepted_current": {
            "type": "boolean"
          },
          "accepted_version_id": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "accepted_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "requires_acceptance": {
            "type": "boolean"
          },
          "must_reaccept": {
            "type": "boolean",
            "description": "True when the user must re-accept before they can proceed (effective_policy is block or forced)."
          },
          "reconsent_policy": {
            "type": "string",
            "enum": [
              "none",
              "notify",
              "forced"
            ],
            "description": "The policy configured on the current published version."
          },
          "effective_policy": {
            "type": "string",
            "enum": [
              "none",
              "notify",
              "forced"
            ],
            "description": "The resolved policy (legacy deadline/block values map to forced)."
          },
          "reconsent_deadline": {
            "type": "string",
            "format": "date-time",
            "nullable": true,
            "description": "Deprecated. Always null for policies created after the simplified UI."
          },
          "past_deadline": {
            "type": "boolean"
          }
        }
      },
      "ComplianceStatusItem": {
        "allOf": [
          {
            "$ref": "#/components/schemas/ConsentStatusItem"
          },
          {
            "type": "object",
            "properties": {
              "reason": {
                "type": "string",
                "enum": [
                  "version_changed",
                  "never_accepted"
                ]
              }
            }
          }
        ]
      },
      "AcceptanceSessionInput": {
        "type": "object",
        "required": [
          "documentSlug",
          "successUrl",
          "cancelUrl"
        ],
        "properties": {
          "documentSlug": {
            "type": "string"
          },
          "version": {
            "type": "string",
            "description": "Pin to a specific version_number. Defaults to the current published version."
          },
          "user": {
            "type": "object",
            "properties": {
              "id": {
                "type": "string",
                "description": "Your user id (becomes user_external_id in the consent record)."
              },
              "email": {
                "type": "string",
                "format": "email"
              },
              "name": {
                "type": "string"
              }
            }
          },
          "customerId": {
            "type": "string"
          },
          "accountId": {
            "type": "string"
          },
          "successUrl": {
            "type": "string",
            "format": "uri",
            "description": "HTTPS URL (http://localhost OK for dev). Termitude appends ?session_id=…"
          },
          "cancelUrl": {
            "type": "string",
            "format": "uri"
          },
          "metadata": {
            "type": "object",
            "additionalProperties": true
          },
          "expiresInMinutes": {
            "type": "integer",
            "minimum": 5,
            "maximum": 1440,
            "default": 30
          },
          "theme": {
            "$ref": "#/components/schemas/HostedAcceptanceTheme"
          }
        }
      },
      "HostedAcceptanceTheme": {
        "type": "object",
        "description": "Per-session appearance overrides. Any field omitted falls back to the workspace branding, then to the Termitude default. Unknown / malformed fields are dropped.",
        "properties": {
          "accentColor": {
            "type": "string",
            "description": "Hex color for primary buttons and accents."
          },
          "buttonTextColor": {
            "type": "string",
            "description": "Hex color for text on accent buttons."
          },
          "background": {
            "type": "string",
            "description": "Hex color for page background."
          },
          "surface": {
            "type": "string",
            "description": "Hex color for cards."
          },
          "textColor": {
            "type": "string",
            "description": "Hex color for body text."
          },
          "mutedTextColor": {
            "type": "string",
            "description": "Hex color for secondary text."
          },
          "borderColor": {
            "type": "string",
            "description": "Hex color for borders and dividers."
          },
          "font": {
            "type": "string",
            "enum": [
              "system",
              "sans",
              "serif",
              "mono"
            ]
          },
          "radius": {
            "type": "integer",
            "minimum": 0,
            "maximum": 24,
            "description": "Corner radius in pixels."
          },
          "logoUrl": {
            "type": "string",
            "format": "uri",
            "nullable": true,
            "description": "Override the workspace logo for this session."
          },
          "showPoweredBy": {
            "type": "boolean",
            "description": "Show or hide the “Powered by Termitude” mark."
          }
        }
      },
      "AcceptanceSession": {
        "type": "object",
        "properties": {
          "sessionId": {
            "type": "string",
            "description": "Looks like `ats_…`."
          },
          "url": {
            "type": "string",
            "format": "uri",
            "description": "Redirect the user here."
          },
          "expiresAt": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "AcceptanceSessionStatus": {
        "type": "object",
        "properties": {
          "sessionId": {
            "type": "string"
          },
          "kind": {
            "type": "string",
            "enum": [
              "acceptance",
              "reconsent"
            ]
          },
          "status": {
            "type": "string",
            "enum": [
              "open",
              "accepted",
              "rejected",
              "expired"
            ]
          },
          "document_id": {
            "type": "string",
            "format": "uuid"
          },
          "version_id": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "accepted_consent_id": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "rejected_reason": {
            "type": "string",
            "nullable": true
          },
          "user_external_id": {
            "type": "string",
            "nullable": true
          },
          "user_email": {
            "type": "string",
            "nullable": true
          },
          "metadata": {
            "type": "object",
            "additionalProperties": true
          },
          "success_url": {
            "type": "string",
            "format": "uri"
          },
          "cancel_url": {
            "type": "string",
            "format": "uri"
          },
          "expires_at": {
            "type": "string",
            "format": "date-time"
          },
          "completed_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      }
    }
  },
  "security": [
    {
      "ApiKeyAuth": []
    },
    {
      "XApiKey": []
    }
  ],
  "paths": {
    "/api/public/v1/documents": {
      "get": {
        "summary": "List documents in the workspace",
        "parameters": [
          {
            "name": "status",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "draft",
                "in_review",
                "published",
                "archived"
              ]
            }
          },
          {
            "name": "category",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "default": 100,
              "minimum": 1,
              "maximum": 500
            }
          },
          {
            "name": "offset",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "default": 0,
              "minimum": 0
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Paginated list of documents",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "documents": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Document"
                      }
                    },
                    "total": {
                      "type": "integer"
                    },
                    "limit": {
                      "type": "integer"
                    },
                    "offset": {
                      "type": "integer"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/public/v1/documents/{slug}": {
      "get": {
        "summary": "Get the current published version of a document",
        "parameters": [
          {
            "name": "slug",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Document + current version",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "document": {
                      "$ref": "#/components/schemas/Document"
                    },
                    "version": {
                      "$ref": "#/components/schemas/DocumentVersion"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found (unpublished or unknown slug)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/public/v1/consent": {
      "post": {
        "summary": "Record a single consent acceptance",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ConsentInput"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ConsentResult"
                }
              }
            }
          },
          "400": {
            "description": "Invalid body",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Document not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/public/v1/consent/batch": {
      "post": {
        "summary": "Record multiple consent acceptances in one call (up to 50)",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "acceptances"
                ],
                "properties": {
                  "acceptances": {
                    "type": "array",
                    "minItems": 1,
                    "maxItems": 50,
                    "items": {
                      "$ref": "#/components/schemas/ConsentInput"
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "All accepted"
          },
          "207": {
            "description": "Partial success — inspect per-item results"
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/public/v1/users/{externalId}/consent-status": {
      "get": {
        "summary": "List documents and whether the user has accepted the current version",
        "description": "`externalId` is matched against `user_external_id` or `user_email`. Optionally filter with `?documentSlugs=a,b,c`.",
        "parameters": [
          {
            "name": "externalId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "documentSlugs",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Comma-separated slugs"
          }
        ],
        "responses": {
          "200": {
            "description": "Per-document acceptance status",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "user": {
                      "type": "string"
                    },
                    "items": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/ConsentStatusItem"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/public/v1/users/{externalId}/compliance-status": {
      "get": {
        "summary": "Summarised compliance status for a user",
        "description": "Returns a single roll-up: `compliant` boolean, an overall `status` (`compliant` | `action_required` | `overdue` | `blocked`), and the documents/versions that still need attention. `externalId` is matched against `user_external_id` or `user_email`. Optional `?documentSlugs=a,b,c` and `?categories=privacy_policy,terms_of_service` filters narrow the scope.",
        "parameters": [
          {
            "name": "externalId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "documentSlugs",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Comma-separated slugs"
          },
          {
            "name": "categories",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Comma-separated document categories"
          }
        ],
        "responses": {
          "200": {
            "description": "Compliance summary",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "user": {
                      "type": "string"
                    },
                    "compliant": {
                      "type": "boolean"
                    },
                    "status": {
                      "type": "string",
                      "enum": [
                        "compliant",
                        "action_required",
                        "overdue",
                        "blocked"
                      ]
                    },
                    "blocked": {
                      "type": "boolean"
                    },
                    "outstanding_count": {
                      "type": "integer"
                    },
                    "requires_reconsent_count": {
                      "type": "integer"
                    },
                    "pending_acceptance_count": {
                      "type": "integer"
                    },
                    "earliest_deadline": {
                      "type": "string",
                      "format": "date-time",
                      "nullable": true
                    },
                    "requires_reconsent": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/ComplianceStatusItem"
                      }
                    },
                    "pending_acceptance": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/ComplianceStatusItem"
                      }
                    },
                    "checked_at": {
                      "type": "string",
                      "format": "date-time"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/public/v1/acceptance-sessions": {
      "post": {
        "summary": "Create a hosted acceptance session",
        "description": "Returns a hosted URL. Redirect the user there; Termitude shows the document, captures Accept/Decline, records the consent, and redirects to your `successUrl` or `cancelUrl` with `?session_id=…` appended. The authoritative outcome is the `acceptance_session.completed` / `acceptance_session.rejected` webhook.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AcceptanceSessionInput"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Session created",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AcceptanceSession"
                }
              }
            }
          },
          "400": {
            "description": "Invalid body",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Document/version not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/public/v1/reconsent-sessions": {
      "post": {
        "summary": "Create a hosted re-consent session",
        "description": "Identical to `/acceptance-sessions` but marks the session as a re-consent. The recorded consent uses method=`forced_reaccept`.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AcceptanceSessionInput"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Session created",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AcceptanceSession"
                }
              }
            }
          },
          "400": {
            "description": "Invalid body (e.g. invalid_redirect_url, no_published_version)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Document/version not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/public/v1/acceptance-sessions/{sessionId}": {
      "get": {
        "summary": "Retrieve a hosted acceptance session",
        "description": "Use this to confirm outcome after the browser redirect (the webhook is still the authoritative signal).",
        "parameters": [
          {
            "name": "sessionId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "The `ats_…` session id."
          }
        ],
        "responses": {
          "200": {
            "description": "Session state",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AcceptanceSessionStatus"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/public/v1/documents/{slug}/versions/{versionId}/integrity": {
      "get": {
        "summary": "Verify the cryptographic integrity proof for a specific published version",
        "description": "Recomputes `sha256(prev_version_hash || canonical_content)` and compares against the stored hash and chain. Use it to validate evidence packs or detect tampering. `ok: false` with `integrity_break_acknowledged: true` means an admin accepted a break (e.g. a legally-required redaction); without acknowledgement, the chain is silently broken.",
        "parameters": [
          {
            "name": "slug",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "versionId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Integrity report",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "version_id": {
                      "type": "string",
                      "format": "uuid"
                    },
                    "hash_version": {
                      "type": "integer"
                    },
                    "self_matches": {
                      "type": "boolean"
                    },
                    "chain_matches": {
                      "type": "boolean"
                    },
                    "expected_hash": {
                      "type": "string"
                    },
                    "stored_hash": {
                      "type": "string",
                      "nullable": true
                    },
                    "prev_version_hash": {
                      "type": "string",
                      "nullable": true
                    },
                    "prev_version_exists": {
                      "type": "boolean"
                    },
                    "integrity_break_acknowledged": {
                      "type": "boolean"
                    },
                    "integrity_break_reason": {
                      "type": "string",
                      "nullable": true
                    },
                    "ok": {
                      "type": "boolean"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Document or version not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    }
  }
}