{
  "openapi": "3.1.0",
  "info": {
    "title": "Feedhook",
    "version": "1.0.0",
    "description": "Turn a YouTube channel into a webhook — a signed JSON POST ~8s after every new video. Free: 1 feed. Pro: $9/mo, 10 feeds."
  },
  "servers": [
    {
      "url": "https://feedhook.walls.sh"
    }
  ],
  "components": {
    "securitySchemes": {
      "apiKey": {
        "type": "http",
        "scheme": "bearer",
        "description": "API key from POST /accounts (fh_…)"
      }
    },
    "schemas": {
      "Error": {
        "type": "object",
        "properties": {
          "error": {
            "type": "string"
          }
        }
      },
      "Account": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "email": {
            "type": "string"
          },
          "plan": {
            "type": "string",
            "enum": [
              "free",
              "pro",
              "internal"
            ]
          },
          "feedLimit": {
            "type": [
              "integer",
              "null"
            ]
          },
          "activeFeeds": {
            "type": "integer"
          },
          "createdAt": {
            "type": "string"
          }
        }
      },
      "Subscription": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "channelId": {
            "type": "string"
          },
          "callbackUrl": {
            "type": "string"
          },
          "topic": {
            "type": "string"
          },
          "state": {
            "type": "string",
            "enum": [
              "pending",
              "active",
              "unsubscribed"
            ]
          },
          "createdAt": {
            "type": "string"
          },
          "verifiedAt": {
            "type": [
              "string",
              "null"
            ]
          },
          "expiresAt": {
            "type": [
              "string",
              "null"
            ]
          },
          "deliveryCount": {
            "type": "integer"
          }
        }
      },
      "WebhookPayload": {
        "type": "object",
        "description": "POSTed to your callbackUrl. Verify x-feedhook-signature: sha256=<hex HMAC-SHA256(raw body, subscription secret)>.",
        "properties": {
          "event": {
            "type": "string",
            "enum": [
              "video.published"
            ]
          },
          "subscriptionId": {
            "type": "string"
          },
          "videoId": {
            "type": "string"
          },
          "channelId": {
            "type": "string"
          },
          "title": {
            "type": "string"
          },
          "author": {
            "type": "string"
          },
          "publishedAt": {
            "type": "string"
          },
          "updatedAt": {
            "type": "string"
          },
          "url": {
            "type": "string"
          },
          "receivedAt": {
            "type": "string"
          }
        }
      }
    }
  },
  "paths": {
    "/accounts": {
      "post": {
        "summary": "Create an account (free: 1 feed). Returns the API key once.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "email"
                ],
                "properties": {
                  "email": {
                    "type": "string"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Account + one-time apiKey",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Account"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "apiKey": {
                          "type": "string"
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "409": {
            "description": "Email exists",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/account": {
      "get": {
        "summary": "Current account, plan, and usage",
        "security": [
          {
            "apiKey": []
          }
        ],
        "responses": {
          "200": {
            "description": "Account",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Account"
                }
              }
            }
          }
        }
      }
    },
    "/subscriptions": {
      "post": {
        "summary": "Turn a channel into a webhook. Response includes the per-subscription signing secret (shown once).",
        "security": [
          {
            "apiKey": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "callbackUrl"
                ],
                "anyOf": [
                  {
                    "required": [
                      "channel"
                    ]
                  },
                  {
                    "required": [
                      "channelId"
                    ]
                  }
                ],
                "properties": {
                  "channel": {
                    "type": "string",
                    "description": "@handle, youtube.com channel URL, or UC… id — resolved server-side"
                  },
                  "channelId": {
                    "type": "string",
                    "pattern": "^UC[0-9A-Za-z_-]{22}$",
                    "description": "legacy: the raw channel id"
                  },
                  "callbackUrl": {
                    "type": "string",
                    "format": "uri"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Subscription + one-time secret",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Subscription"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "secret": {
                          "type": "string"
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "402": {
            "description": "Plan feed limit reached — upgrade via /billing/checkout",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "get": {
        "summary": "List your subscriptions",
        "security": [
          {
            "apiKey": []
          }
        ],
        "responses": {
          "200": {
            "description": "Subscriptions",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "subscriptions": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Subscription"
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/subscriptions/{id}": {
      "get": {
        "summary": "One subscription incl. recent delivery log",
        "security": [
          {
            "apiKey": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Subscription + deliveries",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Subscription"
                }
              }
            }
          }
        }
      },
      "delete": {
        "summary": "Unsubscribe (stops deliveries, releases the feed slot)",
        "security": [
          {
            "apiKey": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Unsubscribed"
          }
        }
      }
    },
    "/subscriptions/{id}/test": {
      "post": {
        "summary": "Send a signed test.ping through the real delivery pipeline (excluded from metrics) — verify your receiver without waiting for a video",
        "security": [
          {
            "apiKey": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "202": {
            "description": "Dispatched; poll the subscription's delivery log for the result"
          }
        }
      }
    },
    "/billing/checkout": {
      "post": {
        "summary": "Create a Stripe Checkout session for Pro ($9/mo, 10 feeds)",
        "security": [
          {
            "apiKey": []
          }
        ],
        "responses": {
          "200": {
            "description": "Checkout URL",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "url": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/metrics": {
      "get": {
        "summary": "Live public usage metrics",
        "responses": {
          "200": {
            "description": "Metrics"
          }
        }
      }
    }
  },
  "webhooks": {
    "video.published": {
      "post": {
        "summary": "Sent to your callbackUrl on every new video",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/WebhookPayload"
              }
            }
          }
        },
        "responses": {
          "2xx": {
            "description": "Acknowledge within 15s; non-2xx retried 5x with backoff"
          }
        }
      }
    }
  }
}
