{
  "openapi": "3.1.0",
  "info": {
    "title": "AnyHook API",
    "version": "1.0.0",
    "description": "Webhook relay API with two surfaces:\n\n**Management API** (/api/v1/) — create apps, query events, trigger replays. Authenticate with: Authorization: Bearer ahk_live_xxx\n\n**Ingress** (https://in.anyhook.net/{user-slug}/{app-slug}) — receives webhooks from Stripe/GitHub/Shopify. No auth required; HMAC signature verified automatically.\n\n**Typical agent workflow:**\n1. POST /api/v1/apps → get inbound_url\n2. Configure inbound_url in Stripe/GitHub/Shopify webhook settings\n3. GET /api/v1/events?app_slug=xxx to monitor delivery\n4. POST /api/v1/events/{id}/replay if delivery failed\n5. DELETE /api/v1/apps/{slug} when done"
  },
  "servers": [
    { "url": "https://anyhook.net", "description": "Management API" },
    { "url": "https://in.anyhook.net", "description": "Ingress (webhook receiver)" }
  ],
  "security": [{ "ApiKeyAuth": [] }],
  "components": {
    "securitySchemes": {
      "ApiKeyAuth": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "ahk_live_xxx",
        "description": "API key from Dashboard Settings > API Keys. Format: ahk_live_<32 alphanumeric chars>. Example header: Authorization: Bearer ahk_live_a1b2c3..."
      }
    },
    "schemas": {
      "App": {
        "type": "object",
        "properties": {
          "id": { "type": "string", "format": "uuid" },
          "name": { "type": "string" },
          "slug": { "type": "string" },
          "source": { "type": "string", "enum": ["stripe", "github", "shopify", "generic"] },
          "inbound_url": { "type": "string", "format": "uri", "description": "Paste this URL into your source platform's webhook settings (Stripe Dashboard, GitHub repo settings, etc)." },
          "destinations": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "url": { "type": "string", "format": "uri" },
                "enabled": { "type": "boolean" }
              }
            }
          },
          "is_active": { "type": "boolean" },
          "created_at": { "type": "string", "format": "date-time" }
        }
      },
      "Event": {
        "type": "object",
        "properties": {
          "id": { "type": "string", "format": "uuid" },
          "app_id": { "type": "string", "format": "uuid" },
          "source": { "type": "string" },
          "event_type": { "type": "string" },
          "status": { "type": "string", "enum": ["queued", "success", "retrying", "failed"] },
          "attempt": { "type": "integer" },
          "max_attempts": { "type": "integer" },
          "is_replay": { "type": "boolean" },
          "outbound_url": { "type": "string" },
          "latency_ms": { "type": "integer" },
          "created_at": { "type": "string", "format": "date-time" },
          "next_retry_at": { "type": ["string", "null"], "format": "date-time" }
        }
      },
      "Error": {
        "type": "object",
        "properties": {
          "error": { "type": "string" }
        }
      }
    }
  },
  "paths": {
    "/api/v1/apps": {
      "get": {
        "operationId": "listApps",
        "summary": "List all apps",
        "description": "Returns all webhook relay apps for the authenticated user.",
        "security": [{ "ApiKeyAuth": [] }],
        "responses": {
          "200": {
            "description": "List of apps",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "apps": { "type": "array", "items": { "$ref": "#/components/schemas/App" } }
                  }
                }
              }
            }
          },
          "401": { "description": "Unauthorized", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }
        }
      },
      "post": {
        "operationId": "createApp",
        "summary": "Create a new webhook relay app",
        "description": "Creates a new webhook relay app and returns its inbound_url.\nPaste inbound_url into Stripe/GitHub/Shopify webhook settings.\nRequired fields: name (string), destination_url (your server endpoint).\nOptional: source ('stripe'|'github'|'shopify'|'generic', default: 'generic').",
        "security": [{ "ApiKeyAuth": [] }],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["name", "destination_url"],
                "properties": {
                  "name": { "type": "string", "description": "Human-readable name for this app." },
                  "destination_url": { "type": "string", "format": "uri", "description": "Your server's endpoint URL that will receive forwarded webhooks." },
                  "source": { "type": "string", "enum": ["stripe", "github", "shopify", "generic"], "default": "generic" }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "App created",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "app": { "$ref": "#/components/schemas/App" }
                  }
                }
              }
            }
          },
          "401": { "description": "Unauthorized" },
          "429": { "description": "Plan limit reached (app count or daily event cap)" }
        }
      }
    },
    "/api/v1/apps/{slug}": {
      "patch": {
        "operationId": "updateApp",
        "summary": "Update an app",
        "description": "Updates destination URLs or toggles the app active state.",
        "security": [{ "ApiKeyAuth": [] }],
        "parameters": [
          { "name": "slug", "in": "path", "required": true, "schema": { "type": "string" } }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "destinations": { "type": "array", "items": { "type": "object" } },
                  "is_active": { "type": "boolean" }
                }
              }
            }
          }
        },
        "responses": {
          "200": { "description": "App updated", "content": { "application/json": { "schema": { "type": "object", "properties": { "app": { "$ref": "#/components/schemas/App" } } } } } },
          "401": { "description": "Unauthorized" },
          "404": { "description": "App not found" }
        }
      },
      "delete": {
        "operationId": "deleteApp",
        "summary": "Delete an app",
        "description": "Permanently deletes a relay app and immediately stops accepting inbound webhooks.\nEvent history is preserved (app_id set to null on existing events).\nUse this to clean up temporary apps created for one-time tasks.",
        "security": [{ "ApiKeyAuth": [] }],
        "parameters": [
          { "name": "slug", "in": "path", "required": true, "schema": { "type": "string" } }
        ],
        "responses": {
          "200": {
            "description": "App deleted",
            "content": { "application/json": { "schema": { "type": "object", "properties": { "deleted": { "type": "boolean" } } } } }
          },
          "401": { "description": "Unauthorized" },
          "404": { "description": "App not found" }
        }
      }
    },
    "/api/v1/events": {
      "get": {
        "operationId": "listEvents",
        "summary": "Query event delivery log",
        "description": "Returns delivery log entries. Useful for monitoring and debugging.\nFilter by: app_slug (string), status ('queued'|'success'|'retrying'|'failed'), limit (int, default 50, max 200).",
        "security": [{ "ApiKeyAuth": [] }],
        "parameters": [
          { "name": "app_slug", "in": "query", "schema": { "type": "string" } },
          { "name": "status", "in": "query", "schema": { "type": "string", "enum": ["queued", "success", "retrying", "failed"] } },
          { "name": "limit", "in": "query", "schema": { "type": "integer", "default": 50, "maximum": 200 } }
        ],
        "responses": {
          "200": {
            "description": "Event list",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "events": { "type": "array", "items": { "$ref": "#/components/schemas/Event" } },
                    "next_cursor": { "type": ["string", "null"] }
                  }
                }
              }
            }
          },
          "401": { "description": "Unauthorized" }
        }
      }
    },
    "/api/v1/events/{id}/replay": {
      "post": {
        "operationId": "replayEvent",
        "summary": "Re-deliver a specific event",
        "description": "Re-delivers a specific event to its original destination URL.\nDoes NOT consume event quota. Use after fixing a downstream error.\nReturns the new event record with is_replay=true.",
        "security": [{ "ApiKeyAuth": [] }],
        "parameters": [
          { "name": "id", "in": "path", "required": true, "schema": { "type": "string", "format": "uuid" } }
        ],
        "responses": {
          "200": {
            "description": "Replay queued",
            "content": { "application/json": { "schema": { "type": "object", "properties": { "event": { "$ref": "#/components/schemas/Event" } } } } }
          },
          "401": { "description": "Unauthorized" },
          "404": { "description": "Event not found" }
        }
      }
    },
    "/{user-slug}/{app-slug}": {
      "post": {
        "servers": [{ "url": "https://in.anyhook.net" }],
        "operationId": "receiveWebhook",
        "summary": "Receive a webhook",
        "description": "Receives an inbound webhook from any source platform. Always returns 200 within 50ms regardless of downstream processing — delivery is asynchronous. Signature is verified automatically based on source-specific headers (Stripe-Signature for Stripe, X-Hub-Signature-256 for GitHub, X-Shopify-Hmac-Sha256 for Shopify). No authentication required on this endpoint.",
        "security": [],
        "parameters": [
          {
            "name": "user-slug",
            "in": "path",
            "required": true,
            "schema": { "type": "string" },
            "description": "Your account slug, visible in Dashboard URL and app inbound_url."
          },
          {
            "name": "app-slug",
            "in": "path",
            "required": true,
            "schema": { "type": "string" },
            "description": "The app slug returned when creating an app via POST /api/v1/apps."
          }
        ],
        "responses": {
          "200": { "description": "Event accepted and queued for async delivery." },
          "404": { "description": "App slug not found or app is inactive/deleted." },
          "413": { "description": "Payload exceeds plan limit: 512KB (Free), 2MB (Pro), 10MB (Scale)." }
        }
      }
    }
  }
}
