{
  "openapi": "3.1.0",
  "info": {
    "title": "HookLab platform core API",
    "version": "0.3.0-rc.5",
    "description": "Core PostgreSQL platform routes. Experimental contract, not a complete listing of administrative routes. All secrets and event bodies must be handled as private data."
  },
  "servers": [{"url": "http://127.0.0.1:8787", "description": "Loopback development only"}],
  "components": {
    "securitySchemes": {"bearerAuth": {"type": "http", "scheme": "bearer"}},
    "schemas": {
      "Error": {"type": "object", "required": ["error"], "properties": {"error": {"type": "string"}, "details": {"type": "array", "items": {"type": "object"}}}},
      "Acceptance": {"type": "object", "required": ["accepted", "duplicate", "eventId", "deliveries"], "properties": {
        "accepted": {"type": "boolean"}, "duplicate": {"type": "boolean"},
        "eventId": {"type": "string", "format": "uuid"}, "deliveries": {"type": "integer"}
      }},
      "ProviderCredential": {"type": "object", "required": ["applicationId", "provider", "secret"], "properties": {
        "applicationId": {"type": "string"}, "provider": {"type": "string", "enum": ["github", "stripe", "feishu", "generic-hmac"]},
        "secret": {"type": "string", "minLength": 16, "maxLength": 1024, "writeOnly": true}
      }}
    }
  },
  "paths": {
    "/health": {"get": {"summary": "Process and PostgreSQL health", "responses": {"200": {"description": "Healthy"}}}},
    "/api/admin/tenants": {"post": {"summary": "Create tenant with bootstrap token", "security": [{"bearerAuth": []}],
      "requestBody": {"required": true, "content": {"application/json": {"schema": {"type": "object", "required": ["id", "name"], "properties": {"id": {"type": "string"}, "name": {"type": "string"}}}}}},
      "responses": {"201": {"description": "Tenant and one-time owner token"}, "401": {"description": "Invalid bootstrap token"}}}},
    "/api/tenants/{tenantId}/provider-credentials": {
      "parameters": [{"in": "path", "name": "tenantId", "required": true, "schema": {"type": "string"}}],
      "get": {"summary": "List provider credential metadata, never secrets", "security": [{"bearerAuth": []}], "responses": {"200": {"description": "Credential metadata"}}},
      "post": {"summary": "Create or rotate provider secret; previous secret remains valid for 24 hours", "security": [{"bearerAuth": []}],
        "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ProviderCredential"}}}},
        "responses": {"200": {"description": "Credential saved without echoing secret"}, "401": {"description": "Unauthorized"}, "403": {"description": "Insufficient role"}}}
    },
    "/api/tenants/{tenantId}/provider-credentials/{applicationId}/{provider}": {
      "parameters": [
        {"in": "path", "name": "tenantId", "required": true, "schema": {"type": "string"}},
        {"in": "path", "name": "applicationId", "required": true, "schema": {"type": "string"}},
        {"in": "path", "name": "provider", "required": true, "schema": {"type": "string"}}
      ],
      "delete": {"summary": "Revoke a provider credential", "security": [{"bearerAuth": []}], "responses": {"200": {"description": "Deleted"}}}
    },
    "/api/tenants/{tenantId}/applications/{applicationId}/providers/{provider}": {
      "parameters": [
        {"in": "path", "name": "tenantId", "required": true, "schema": {"type": "string"}},
        {"in": "path", "name": "applicationId", "required": true, "schema": {"type": "string"}},
        {"in": "path", "name": "provider", "required": true, "schema": {"type": "string", "enum": ["github", "stripe", "feishu", "generic-hmac"]}}
      ],
      "post": {"summary": "Accept a verified provider webhook", "description": "No bearer token: the provider-specific raw-body signature is mandatory. Generic HMAC also requires a fresh timestamp and delivery ID.",
        "requestBody": {"required": true, "content": {"application/json": {"schema": {}}}},
        "responses": {"202": {"description": "New event", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/Acceptance"}}}},
          "200": {"description": "Exact duplicate"}, "401": {"description": "Invalid signature or freshness"},
          "409": {"description": "Reused delivery ID with changed event"}, "422": {"description": "Invalid payload or contract"},
          "429": {"description": "Tenant quota exceeded"}}}
    },
    "/api/tenants/{tenantId}/applications/{applicationId}/events/{eventType}": {
      "parameters": [
        {"in": "path", "name": "tenantId", "required": true, "schema": {"type": "string"}},
        {"in": "path", "name": "applicationId", "required": true, "schema": {"type": "string"}},
        {"in": "path", "name": "eventType", "required": true, "schema": {"type": "string"}},
        {"in": "header", "name": "Idempotency-Key", "required": true, "schema": {"type": "string", "maxLength": 128}}
      ],
      "post": {"summary": "Publish an application event", "security": [{"bearerAuth": []}],
        "requestBody": {"required": true, "content": {"application/json": {"schema": {}}, "application/cloudevents+json": {"schema": {"type": "object"}}}},
        "responses": {"202": {"description": "New event", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/Acceptance"}}}},
          "200": {"description": "Exact duplicate"}, "409": {"description": "Idempotency conflict"},
          "422": {"description": "Contract violation"}, "429": {"description": "Tenant quota exceeded"}}}
    },
    "/api/tenants/{tenantId}/events/{eventId}/timeline": {
      "parameters": [
        {"in": "path", "name": "tenantId", "required": true, "schema": {"type": "string"}},
        {"in": "path", "name": "eventId", "required": true, "schema": {"type": "string", "format": "uuid"}}
      ],
      "get": {"summary": "Redacted event and delivery timeline", "security": [{"bearerAuth": []}],
        "responses": {"200": {"description": "Event metadata, deliveries and up to 500 attempts"}, "404": {"description": "No event in this tenant"}}}
    },
    "/api/tenants/{tenantId}/slo": {
      "parameters": [{"in": "path", "name": "tenantId", "required": true, "schema": {"type": "string"}}],
      "get": {"summary": "24h delivery success and latency summary", "security": [{"bearerAuth": []}],
        "responses": {"200": {"description": "Attempt and acceptance-to-delivery p95 values"}}}
    },
    "/metrics": {"get": {"summary": "Prometheus metrics", "security": [{"bearerAuth": []}],
      "responses": {"200": {"description": "Prometheus text metrics"}, "401": {"description": "Invalid metrics token"}}}}
  }
}
