{
  "openapi": "3.1.0",
  "info": {
    "title": "StackWeaver API",
    "version": "1.0.0-beta",
    "description": "Read-only access to one workspace's Evidence Objects, evidence requests and per-framework counts, signed webhooks, and the Partner API.\n\nHonesty: StackWeaver does not audit, certify or endorse anyone. Nothing here is an audit opinion, a certification or a guarantee of compliance. There is no overall score: counts are per framework only. Control-library mappings stay drafts until a named reviewer signs them off (mappings_reviewed=false).\n\nRead API keys: a workspace owner or admin creates a key in the portal (API & webhooks). The key (swk_...) is shown once; StackWeaver stores only its SHA-256 fingerprint. Each key has permissions (evidence:read, requests:read, posture:read, obligations:read), an optional expiry and can be revoked at any time. Send it as Authorization: Bearer swk_... from your server only (the API sends no CORS headers). Limit: 600 requests per 10 minutes per key.\n\nWebhooks: each delivery has the header StackWeaver-Signature: t=<unix seconds>,v1=<hex HMAC-SHA256 of '<t>.<raw body>' using your endpoint's signing secret (whsec_...)>. Recompute it over the raw body, compare in constant time, and reject anything older than 5 minutes. The signing secret is shown once in the portal; rotate it to get a new one. Failed deliveries are retried after 2, 4, 8 ... 256 minutes (8 attempts); 20 failures in a row switch the endpoint off. Targets must be https on port 443 or 8443 with a public host name."
  },
  "servers": [
    {
      "url": "https://portal.stackweaver.cloud"
    }
  ],
  "security": [
    {
      "apiKey": []
    }
  ],
  "paths": {
    "/api/v1/evidence": {
      "get": {
        "operationId": "listEvidence",
        "summary": "List Evidence Objects (oldest first)",
        "x-availability": "beta",
        "x-permission": "evidence:read",
        "description": "Withdrawn evidence is left out unless you ask for status=withdrawn. Metadata only; files are never returned.",
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 50
            }
          },
          {
            "name": "status",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "submitted",
                "validated",
                "accepted",
                "rejected",
                "signed_off",
                "superseded",
                "withdrawn"
              ]
            }
          },
          {
            "name": "updated_since",
            "in": "query",
            "description": "Only items changed at or after this time (ISO 8601).",
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "description": "The next_cursor value from the previous page.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "A page of Evidence Objects",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data",
                    "next_cursor"
                  ],
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/EvidenceObject"
                      }
                    },
                    "next_cursor": {
                      "type": [
                        "string",
                        "null"
                      ]
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid parameter",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing, invalid, expired or revoked API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The key does not have the permission this endpoint needs",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit: 600 requests per 10 minutes per key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "502": {
            "description": "Temporary problem; try again",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/evidence/{eo_id}": {
      "get": {
        "operationId": "getEvidence",
        "summary": "Get one Evidence Object with its review and sign-off decisions",
        "x-availability": "beta",
        "x-permission": "evidence:read",
        "parameters": [
          {
            "name": "eo_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^EO-\\d{4}-\\d{1,12}$",
              "example": "EO-2026-000184"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The Evidence Object",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/EvidenceObjectDetail"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid parameter",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing, invalid, expired or revoked API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The key does not have the permission this endpoint needs",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "No evidence with that ID in this workspace",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit: 600 requests per 10 minutes per key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "502": {
            "description": "Temporary problem; try again",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/requests": {
      "get": {
        "operationId": "listRequests",
        "summary": "List evidence requests (oldest first)",
        "x-availability": "beta",
        "x-permission": "requests:read",
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 50
            }
          },
          {
            "name": "status",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "open",
                "submitted",
                "accepted",
                "rejected",
                "signed_off",
                "cancelled"
              ]
            }
          },
          {
            "name": "updated_since",
            "in": "query",
            "description": "Only items changed at or after this time (ISO 8601).",
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "description": "The next_cursor value from the previous page.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "A page of evidence requests",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data",
                    "next_cursor"
                  ],
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/EvidenceRequest"
                      }
                    },
                    "next_cursor": {
                      "type": [
                        "string",
                        "null"
                      ]
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid parameter",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing, invalid, expired or revoked API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The key does not have the permission this endpoint needs",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit: 600 requests per 10 minutes per key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "502": {
            "description": "Temporary problem; try again",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/posture": {
      "get": {
        "operationId": "getPosture",
        "summary": "Per-framework counts (no overall score)",
        "x-availability": "beta",
        "x-permission": "posture:read",
        "responses": {
          "200": {
            "description": "Counts per framework",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Posture"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid parameter",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing, invalid, expired or revoked API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The key does not have the permission this endpoint needs",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit: 600 requests per 10 minutes per key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "502": {
            "description": "Temporary problem; try again",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/obligations": {
      "get": {
        "operationId": "listObligations",
        "summary": "Regulatory obligations (Atlas catalogue, reviewed entries only)",
        "x-availability": "beta",
        "x-permission": "obligations:read",
        "description": "The Atlas catalogue of regulatory obligations (CBN AML, NDPA, ISO 27001, SOC 2, PCI DSS, Kenya, Ghana, South Africa, Egypt and the rest), with the controls each obligation maps to. Catalogue is global: this is the same answer regardless of which workspace the key belongs to. Only entries with a named human review are returned; drafts and retired items are never served.",
        "parameters": [
          { "name": "framework", "in": "query", "description": "Framework code (SOC2, ISO27001, PCI_DSS, NDPA, CBN_AML, GDPR, HIPAA, EU_AI_ACT, DORA, ATLAS_KE, ATLAS_GH, ATLAS_ZA, ATLAS_EG, ATLAS_EG_FINTECH).", "schema": { "type": "string" } },
          { "name": "limit", "in": "query", "schema": { "type": "integer", "minimum": 1, "maximum": 100, "default": 50 } },
          { "name": "updated_since", "in": "query", "schema": { "type": "string", "format": "date-time" } },
          { "name": "cursor", "in": "query", "schema": { "type": "string" } }
        ],
        "responses": {
          "200": { "description": "Obligations", "content": { "application/json": { "schema": { "type": "object", "properties": { "data": { "type": "array", "items": { "$ref": "#/components/schemas/Obligation" } }, "next_cursor": { "type": ["string", "null"] } } } } } },
          "400": { "description": "Invalid parameter", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
          "401": { "description": "Missing, invalid, expired or revoked API key", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
          "403": { "description": "The key does not have the obligations:read permission", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
          "429": { "description": "Rate limit: 600 requests per 10 minutes per key", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
          "502": { "description": "Temporary problem; try again", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }
        }
      }
    },
    "/api/v1/obligations/{id}": {
      "get": {
        "operationId": "getObligation",
        "summary": "One obligation (reviewed only)",
        "x-availability": "beta",
        "x-permission": "obligations:read",
        "parameters": [
          { "name": "id", "in": "path", "required": true, "schema": { "type": "string", "format": "uuid" } }
        ],
        "responses": {
          "200": { "description": "The obligation", "content": { "application/json": { "schema": { "type": "object", "properties": { "data": { "$ref": "#/components/schemas/Obligation" } } } } } },
          "400": { "description": "Invalid obligation ID", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
          "401": { "description": "Missing, invalid, expired or revoked API key", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
          "403": { "description": "The key does not have the obligations:read permission", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
          "404": { "description": "No obligation with that ID (or it is not reviewed)", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
          "429": { "description": "Rate limit", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
          "502": { "description": "Temporary problem; try again", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }
        }
      }
    },
    "/api/partner/engagements": {
      "get": {
        "operationId": "partnerListEngagements",
        "summary": "Partner API: engagements your firm owns",
        "x-availability": "available",
        "security": [
          {
            "clerkSession": []
          }
        ],
        "description": "Runs with your own session; the database only returns your firm's engagements.",
        "responses": {
          "200": {
            "description": "Engagements with evidence counts",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "400": {
            "description": "Invalid input",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Not signed in as a partner firm account",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found for your firm",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "502": {
            "description": "Temporary problem; try again",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/partner/evidence-package": {
      "get": {
        "operationId": "partnerGetPackage",
        "summary": "Partner API: the evidence package for one engagement",
        "x-availability": "available",
        "security": [
          {
            "clerkSession": []
          }
        ],
        "parameters": [
          {
            "name": "engagement_id",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The package",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "400": {
            "description": "Invalid input",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Not signed in as a partner firm account",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found for your firm",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "502": {
            "description": "Temporary problem; try again",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/partner/{id}/review": {
      "post": {
        "operationId": "partnerReviewEvidence",
        "summary": "Partner API: approve, reject or request changes on an Evidence Object",
        "x-availability": "available",
        "security": [
          {
            "clerkSession": []
          }
        ],
        "description": "The reviewer recorded is the signed-in caller. Signed-off, superseded or withdrawn evidence cannot be reviewed here.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Engagement ID",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "evidence_object_id",
                  "decision"
                ],
                "properties": {
                  "evidence_object_id": {
                    "type": "string",
                    "format": "uuid"
                  },
                  "decision": {
                    "type": "string",
                    "enum": [
                      "approve",
                      "reject",
                      "request_changes"
                    ]
                  },
                  "note": {
                    "type": "string",
                    "maxLength": 2000
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Review recorded",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "400": {
            "description": "Invalid input",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Not signed in as a partner firm account",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found for your firm",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "502": {
            "description": "Temporary problem; try again",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/partner/{id}/request-evidence": {
      "post": {
        "operationId": "partnerRequestEvidence",
        "summary": "Partner API: ask the client for evidence on a control",
        "x-availability": "available",
        "security": [
          {
            "clerkSession": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Engagement ID",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "control_id",
                  "message"
                ],
                "properties": {
                  "control_id": {
                    "type": "string",
                    "maxLength": 100
                  },
                  "message": {
                    "type": "string",
                    "maxLength": 2000
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Request opened",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "400": {
            "description": "Invalid input",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Not signed in as a partner firm account",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found for your firm",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "502": {
            "description": "Temporary problem; try again",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/embed/status/{slug}": {
      "get": {
        "operationId": "embedStatus",
        "summary": "Embeddable status box (HTML for an iframe)",
        "x-availability": "beta",
        "security": [],
        "description": "Only for a published trust page whose company name and address StackWeaver approved, with the embed switched on. Only the websites the owner listed may show it (Content-Security-Policy frame-ancestors). Shows per-framework counts, the disclaimer and a link to the full trust page.",
        "parameters": [
          {
            "name": "slug",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "HTML",
            "content": {
              "text/html": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "404": {
            "description": "Not available",
            "content": {
              "text/html": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests"
          }
        }
      }
    }
  },
  "webhooks": {
    "evidence.submitted": {
      "post": {
        "summary": "A new Evidence Object was submitted",
        "x-availability": "beta",
        "description": "Sent as an HTTPS POST with a JSON body. Verify StackWeaver-Signature before trusting it (see the Webhooks section of info.description). Reply with any 2xx status within 8 seconds. Redirects are not followed. Delivery is at least once: de-duplicate on the event id.",
        "parameters": [
          {
            "name": "StackWeaver-Signature",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "example": "t=1760000000,v1=5257a869e7ecebeda32affa62cdca3fa51cad7e77a0e56ff536d0ce8e108d8bd"
            }
          },
          {
            "name": "StackWeaver-Event",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "enum": [
                "evidence.submitted"
              ]
            }
          },
          {
            "name": "StackWeaver-Event-Id",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "StackWeaver-Delivery",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/WebhookEnvelope"
                  },
                  {
                    "type": "object",
                    "properties": {
                      "type": {
                        "const": "evidence.submitted"
                      },
                      "data": {
                        "$ref": "#/components/schemas/WebhookEvidence"
                      }
                    }
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "2XX": {
            "description": "Received"
          }
        }
      }
    },
    "evidence.accepted": {
      "post": {
        "summary": "A reviewer accepted an Evidence Object",
        "x-availability": "beta",
        "description": "Sent as an HTTPS POST with a JSON body. Verify StackWeaver-Signature before trusting it (see the Webhooks section of info.description). Reply with any 2xx status within 8 seconds. Redirects are not followed. Delivery is at least once: de-duplicate on the event id.",
        "parameters": [
          {
            "name": "StackWeaver-Signature",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "example": "t=1760000000,v1=5257a869e7ecebeda32affa62cdca3fa51cad7e77a0e56ff536d0ce8e108d8bd"
            }
          },
          {
            "name": "StackWeaver-Event",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "enum": [
                "evidence.accepted"
              ]
            }
          },
          {
            "name": "StackWeaver-Event-Id",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "StackWeaver-Delivery",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/WebhookEnvelope"
                  },
                  {
                    "type": "object",
                    "properties": {
                      "type": {
                        "const": "evidence.accepted"
                      },
                      "data": {
                        "$ref": "#/components/schemas/WebhookEvidence"
                      }
                    }
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "2XX": {
            "description": "Received"
          }
        }
      }
    },
    "evidence.signed_off": {
      "post": {
        "summary": "An Evidence Object was signed off",
        "x-availability": "beta",
        "description": "Sent as an HTTPS POST with a JSON body. Verify StackWeaver-Signature before trusting it (see the Webhooks section of info.description). Reply with any 2xx status within 8 seconds. Redirects are not followed. Delivery is at least once: de-duplicate on the event id.",
        "parameters": [
          {
            "name": "StackWeaver-Signature",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "example": "t=1760000000,v1=5257a869e7ecebeda32affa62cdca3fa51cad7e77a0e56ff536d0ce8e108d8bd"
            }
          },
          {
            "name": "StackWeaver-Event",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "enum": [
                "evidence.signed_off"
              ]
            }
          },
          {
            "name": "StackWeaver-Event-Id",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "StackWeaver-Delivery",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/WebhookEnvelope"
                  },
                  {
                    "type": "object",
                    "properties": {
                      "type": {
                        "const": "evidence.signed_off"
                      },
                      "data": {
                        "$ref": "#/components/schemas/WebhookEvidence"
                      }
                    }
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "2XX": {
            "description": "Received"
          }
        }
      }
    },
    "request.created": {
      "post": {
        "summary": "An evidence request was created",
        "x-availability": "beta",
        "description": "Sent as an HTTPS POST with a JSON body. Verify StackWeaver-Signature before trusting it (see the Webhooks section of info.description). Reply with any 2xx status within 8 seconds. Redirects are not followed. Delivery is at least once: de-duplicate on the event id.",
        "parameters": [
          {
            "name": "StackWeaver-Signature",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "example": "t=1760000000,v1=5257a869e7ecebeda32affa62cdca3fa51cad7e77a0e56ff536d0ce8e108d8bd"
            }
          },
          {
            "name": "StackWeaver-Event",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "enum": [
                "request.created"
              ]
            }
          },
          {
            "name": "StackWeaver-Event-Id",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "StackWeaver-Delivery",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/WebhookEnvelope"
                  },
                  {
                    "type": "object",
                    "properties": {
                      "type": {
                        "const": "request.created"
                      },
                      "data": {
                        "$ref": "#/components/schemas/WebhookRequest"
                      }
                    }
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "2XX": {
            "description": "Received"
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "apiKey": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "swk_<48 hex>",
        "description": "Workspace API key, shown once when created."
      },
      "clerkSession": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "JWT",
        "description": "A StackWeaver portal session token (partner firm accounts)."
      }
    },
    "schemas": {
      "Error": {
        "type": "object",
        "required": [
          "error"
        ],
        "properties": {
          "error": {
            "type": "string"
          }
        }
      },
      "ControlRef": {
        "type": "object",
        "properties": {
          "framework": {
            "type": [
              "string",
              "null"
            ]
          },
          "control_ref": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "library_status": {
            "type": "string",
            "description": "draft until a named reviewer signs it off"
          }
        }
      },
      "EvidenceObject": {
        "type": "object",
        "properties": {
          "eo_id": {
            "type": "string",
            "example": "EO-2026-000184"
          },
          "version": {
            "type": "integer"
          },
          "supersedes": {
            "type": [
              "string",
              "null"
            ]
          },
          "title": {
            "type": [
              "string",
              "null"
            ]
          },
          "description": {
            "type": [
              "string",
              "null"
            ]
          },
          "evidence_type": {
            "type": "string"
          },
          "source_type": {
            "type": "string",
            "enum": [
              "upload",
              "collector",
              "scan",
              "attestation",
              "api"
            ]
          },
          "status": {
            "type": "string",
            "enum": [
              "submitted",
              "validated",
              "accepted",
              "rejected",
              "signed_off",
              "superseded",
              "withdrawn"
            ]
          },
          "content_type": {
            "type": [
              "string",
              "null"
            ]
          },
          "file_size": {
            "type": [
              "integer",
              "null"
            ]
          },
          "integrity": {
            "type": "object",
            "properties": {
              "algorithm": {
                "const": "sha256"
              },
              "hash": {
                "type": "string",
                "pattern": "^[0-9a-f]{64}$"
              }
            }
          },
          "observed_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          },
          "controls": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ControlRef"
            }
          }
        }
      },
      "EvidenceObjectDetail": {
        "allOf": [
          {
            "$ref": "#/components/schemas/EvidenceObject"
          },
          {
            "type": "object",
            "properties": {
              "decisions": {
                "type": "array",
                "items": {
                  "type": "object",
                  "properties": {
                    "decision": {
                      "type": "string",
                      "enum": [
                        "accepted",
                        "rejected",
                        "signed_off"
                      ]
                    },
                    "evidence_version": {
                      "type": "integer"
                    },
                    "signer_role": {
                      "type": [
                        "string",
                        "null"
                      ]
                    },
                    "self_review": {
                      "type": "boolean"
                    },
                    "signed_at": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "chain_hash": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          }
        ]
      },
      "EvidenceRequest": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "title": {
            "type": "string"
          },
          "description": {
            "type": [
              "string",
              "null"
            ]
          },
          "status": {
            "type": "string",
            "enum": [
              "open",
              "submitted",
              "accepted",
              "rejected",
              "signed_off",
              "cancelled"
            ]
          },
          "due_on": {
            "type": "string",
            "format": "date"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          },
          "control": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/ControlRef"
              },
              {
                "type": "null"
              }
            ]
          },
          "latest_evidence": {
            "type": [
              "string",
              "null"
            ],
            "description": "EO ID of the latest submission"
          }
        }
      },
      "Obligation": {
        "type": "object",
        "description": "One regulatory obligation from the Atlas catalogue, with its source document reference and the controls it maps to.",
        "properties": {
          "id": { "type": "string", "format": "uuid" },
          "ref": { "type": "string", "description": "Reference as the source uses it (A.8.15, Reg. 22, s.24(1), 10.2.1)." },
          "title": { "type": "string" },
          "summary": { "type": ["string", "null"], "description": "Paraphrase, not a copy of the source." },
          "framework": { "type": "string", "description": "Framework code (SOC2, ISO27001, PCI_DSS, NDPA, CBN_AML, ATLAS_KE, ...)." },
          "source": {
            "type": ["object", "null"],
            "description": "The source document this obligation cites.",
            "properties": {
              "reference": { "type": ["string", "null"] },
              "title": { "type": "string" },
              "issuing_body": { "type": ["string", "null"] },
              "url": { "type": ["string", "null"], "format": "uri" }
            }
          },
          "controls": {
            "type": "array",
            "description": "Controls this obligation maps to; strength is 'full', 'partial' or 'supporting'. Reviewed mappings only.",
            "items": {
              "type": "object",
              "properties": {
                "control_id": { "type": "string", "format": "uuid" },
                "control_ref": { "type": "string" },
                "strength": { "type": "string", "enum": ["full", "partial", "supporting"] }
              }
            }
          },
          "reviewed_on": { "type": ["string", "null"], "format": "date" },
          "created_at": { "type": "string", "format": "date-time" },
          "updated_at": { "type": "string", "format": "date-time" }
        }
      },
      "Posture": {
        "type": "object",
        "properties": {
          "workspace": {
            "type": "string"
          },
          "generated_at": {
            "type": "string",
            "format": "date-time"
          },
          "note": {
            "type": "string"
          },
          "frameworks": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "code": {
                  "type": "string"
                },
                "name": {
                  "type": "string"
                },
                "controls_in_scope": {
                  "type": "integer"
                },
                "controls_with_signed_off_evidence": {
                  "type": "integer"
                },
                "controls_with_any_evidence": {
                  "type": "integer"
                },
                "mappings_reviewed": {
                  "type": "boolean",
                  "description": "false = the control-library mappings are drafts"
                }
              }
            }
          }
        }
      },
      "WebhookEnvelope": {
        "type": "object",
        "required": [
          "id",
          "type",
          "created_at",
          "workspace_id",
          "data"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "description": "Event id; the same for every endpoint and every retry"
          },
          "type": {
            "type": "string"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "workspace_id": {
            "type": "string",
            "format": "uuid"
          },
          "data": {
            "type": "object"
          }
        }
      },
      "WebhookEvidence": {
        "type": "object",
        "properties": {
          "eo_id": {
            "type": "string"
          },
          "version": {
            "type": "integer"
          },
          "title": {
            "type": [
              "string",
              "null"
            ]
          },
          "evidence_type": {
            "type": "string"
          },
          "source_type": {
            "type": "string"
          },
          "status": {
            "type": "string"
          },
          "sha256": {
            "type": "string"
          },
          "occurred_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "WebhookRequest": {
        "type": "object",
        "properties": {
          "request_id": {
            "type": "string",
            "format": "uuid"
          },
          "title": {
            "type": "string"
          },
          "status": {
            "type": "string"
          },
          "due_on": {
            "type": "string",
            "format": "date"
          },
          "control": {
            "type": [
              "object",
              "null"
            ],
            "properties": {
              "framework": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "control_ref": {
                "type": "string"
              }
            }
          },
          "occurred_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      }
    }
  }
}
