{"openapi":"3.0.3","info":{"title":"Webbu API","version":"1.0.0","description":"Webhook buffering and batched delivery API. Webbu receives webhooks, buffers them by a grouping key, and delivers batched payloads to configured HTTP destinations with retry and DLQ support.","contact":{"name":"Webbu","url":"https://webbu.dev","email":"support@webbu.dev"},"license":{"name":"Proprietary"}},"servers":[{"url":"https://webbu.dev/api","description":"Production"},{"url":"http://localhost:5001/webhook-buffer/us-central1/api","description":"Local emulator"}],"tags":[{"name":"Ingest","description":"Webhook ingestion endpoints"},{"name":"Projects","description":"Project management"},{"name":"Buffers","description":"Buffer configuration"},{"name":"Analytics","description":"Metrics and analytics"},{"name":"DLQ","description":"Dead letter queue management"},{"name":"API Keys","description":"API key management"},{"name":"Credits","description":"Credit and billing"},{"name":"Payments","description":"Subscription and payment management"},{"name":"Customers","description":"Customer management"},{"name":"Health","description":"Health check"}],"components":{"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","in":"header","name":"X-API-Key","description":"Webbu API key (wbk_ prefix)"}},"schemas":{"Error":{"type":"object","required":["error"],"properties":{"error":{"type":"string","description":"Error message"},"code":{"type":"string","description":"Machine-readable error code, when the error has one (e.g. `payload_unavailable`)"}}},"Customer":{"type":"object","properties":{"customerId":{"type":"string"},"name":{"type":"string"},"enabled":{"type":"boolean"},"credits":{"type":"number"},"creditLimit":{"type":"number"},"creditsUsedThisMonth":{"type":"number"},"createdAt":{"type":"number"}}},"Project":{"type":"object","properties":{"projectId":{"type":"string"},"customerId":{"type":"string"},"name":{"type":"string"},"enabled":{"type":"boolean"},"createdAt":{"type":"number"},"updatedAt":{"type":"number"}}},"Buffer":{"type":"object","properties":{"bufferId":{"type":"string"},"customerId":{"type":"string"},"projectId":{"type":"string"},"name":{"type":"string"},"enabled":{"type":"boolean"},"groupKey":{"type":"object","description":"Group key extraction configuration"},"batch":{"type":"object","properties":{"maxItems":{"type":"integer"},"maxBytes":{"type":"integer"},"idleTimeoutMs":{"type":"integer"},"maxWaitFromFirstMs":{"type":"integer"}}},"overflow":{"type":"object"},"delivery":{"type":"object","properties":{"url":{"type":"string","format":"uri"},"method":{"type":"string"}}},"createdAt":{"type":"number"},"updatedAt":{"type":"number"}}},"DlqItem":{"type":"object","properties":{"dlqId":{"type":"string"},"customerId":{"type":"string"},"projectId":{"type":"string"},"bufferId":{"type":"string"},"groupId":{"type":"string"},"groupKey":{"type":"string"},"reason":{"type":"string","enum":["max_retries_exceeded","non_retryable_error"]},"lastError":{"type":"string","description":"Error of the last delivery attempt (automatic or manual retry)"},"lastStatusCode":{"type":"integer","description":"HTTP status of the last delivery attempt (0 for network errors)"},"attempt":{"type":"integer","description":"Zero-based index of the last automatic attempt (automatic attempts made = attempt + 1)"},"itemCount":{"type":"integer","description":"Number of events in the failed batch"},"payloadSummary":{"type":"object","properties":{"messageIds":{"type":"array","items":{"type":"string"}},"firstPayloadPreview":{"type":"string","description":"First 500 characters of the first event's payload (JSON)"}}},"payloadStored":{"type":"boolean","description":"Whether the full batch is stored and can be retried. Missing on entries created\nbefore batches were stored; those return `payload_unavailable` on retry.\n"},"batch":{"type":"object","properties":{"groupHash":{"type":"string"},"segment":{"type":"integer"},"firstItemAt":{"type":"number"},"lastActivityAt":{"type":"number"},"groupVersion":{"type":"integer"},"payloadBytes":{"type":"integer","description":"Total size of the stored event payloads"}}},"retryCount":{"type":"integer","description":"Number of failed manual retries"},"lastRetryAt":{"type":"number"},"createdAt":{"type":"number"}}},"DeliveryBatch":{"type":"object","description":"Body delivered to a buffer's destination","properties":{"webbu":{"type":"object","properties":{"customerId":{"type":"string"},"projectId":{"type":"string"},"bufferId":{"type":"string"},"groupKey":{"type":"string"},"groupHash":{"type":"string"},"segment":{"type":"integer"}}},"window":{"type":"object","properties":{"firstItemAt":{"type":"number"},"lastActivityAt":{"type":"number"},"itemsCount":{"type":"integer"}}},"items":{"type":"array","items":{"type":"object","properties":{"messageId":{"type":"string"},"receivedAt":{"type":"number"},"payload":{}}}}}},"DlqRetryResult":{"type":"object","properties":{"success":{"type":"boolean","example":true},"status":{"type":"string","enum":["delivered"]},"message":{"type":"string","example":"Batch redelivered"},"dlqId":{"type":"string"},"itemCount":{"type":"integer"},"statusCode":{"type":"integer","description":"HTTP status returned by the destination"},"durationMs":{"type":"number"}}},"DlqRetryFailure":{"type":"object","properties":{"error":{"type":"string"},"code":{"type":"string","enum":["delivery_failed"]},"dlqId":{"type":"string"},"itemCount":{"type":"integer"},"statusCode":{"type":"integer","description":"HTTP status returned by the destination (0 for network errors)"},"durationMs":{"type":"number"},"lastError":{"type":"string"},"errorClass":{"type":"string"},"retryCount":{"type":"integer","description":"Failed manual retries of this item, including this one"}}},"Plan":{"type":"object","properties":{"planId":{"type":"string"},"name":{"type":"string"},"priceInCents":{"type":"integer","description":"Legacy single-currency monthly price (in `currency`, BRL). Use `prices`."},"currency":{"type":"string","description":"Currency of `priceInCents`"},"prices":{"type":"object","description":"Monthly price per currency, in cents (e.g. BRL 4900, USD 990)","properties":{"BRL":{"type":"integer"},"USD":{"type":"integer"}}},"creditsPerCycle":{"type":"number"},"isActive":{"type":"boolean"}}},"Subscription":{"type":"object","properties":{"subscriptionId":{"type":"string"},"customerId":{"type":"string"},"planId":{"type":"string"},"status":{"type":"string","enum":["PENDING","ACTIVE","CANCELLING","CANCELLED","PAST_DUE"]},"currentPeriodStart":{"type":"number"},"currentPeriodEnd":{"type":"number"},"autoRenew":{"type":"boolean"}}},"Invoice":{"type":"object","properties":{"invoiceId":{"type":"string"},"customerId":{"type":"string"},"subscriptionId":{"type":"string"},"status":{"type":"string"},"amount":{"type":"number"},"paidAt":{"type":"number","nullable":true},"createdAt":{"type":"number"}}}}},"security":[{"ApiKeyAuth":[]}],"paths":{"/v1/admin/customers":{"post":{"tags":["Customers"],"summary":"Create a customer","description":"Creates a new customer record. Requires owner role.","operationId":"createCustomer","security":[{"ApiKeyAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["customerId","name"],"properties":{"customerId":{"type":"string"},"name":{"type":"string"}}}}}},"responses":{"201":{"description":"Customer created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Customer"}}}},"400":{"description":"Missing required fields","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Customer already exists","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/admin/customers/{customerId}":{"get":{"tags":["Customers"],"summary":"Get a customer","description":"Returns a single customer by ID.","operationId":"getCustomer","security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"customerId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Customer details","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Customer"}}}},"404":{"description":"Customer not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/admin/projects":{"post":{"tags":["Projects"],"summary":"Create a project","description":"Creates a new project for the authenticated customer. Requires owner or admin role.\nThe caller chooses the project ID (the dashboard uses `p_<timestamp>_<random>`).\n","operationId":"createProject","security":[{"ApiKeyAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["projectId","name"],"properties":{"projectId":{"type":"string","description":"Unique project ID chosen by the caller"},"customerId":{"type":"string","description":"Optional. Defaults to the authenticated customer; any other value is rejected with 403."},"name":{"type":"string"}}}}}},"responses":{"201":{"description":"Project created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Project"}}}},"400":{"description":"Missing required fields or customer not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Project already exists","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"get":{"tags":["Projects"],"summary":"List projects","description":"Returns all projects for a customer.","operationId":"listProjects","security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"customerId","in":"query","required":false,"schema":{"type":"string"},"description":"The customer ID to list projects for"}],"responses":{"200":{"description":"List of projects","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/Project"}}}}},"400":{"description":"Missing customerId","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/admin/projects/{projectId}":{"get":{"tags":["Projects"],"summary":"Get a project","description":"Returns a single project by ID.","operationId":"getProject","security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"projectId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Project details","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Project"}}}},"404":{"description":"Project not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"patch":{"tags":["Projects"],"summary":"Update a project","description":"Updates a project's name or enabled status.","operationId":"updateProject","security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"projectId","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string"},"enabled":{"type":"boolean"}}}}}},"responses":{"200":{"description":"Updated project","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Project"}}}},"404":{"description":"Project not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"delete":{"tags":["Projects"],"summary":"Delete a project","description":"Deletes a project. Fails if the project still has buffers.","operationId":"deleteProject","security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"projectId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"204":{"description":"Project deleted"},"400":{"description":"Cannot delete project with existing buffers","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Project not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/admin/buffers":{"post":{"tags":["Buffers"],"summary":"Create a buffer","description":"Creates a new buffer with batching rules, delivery configuration, and overflow settings.\nThe buffer is validated against the BufferSchema before creation.\n","operationId":"createBuffer","security":[{"ApiKeyAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["bufferId","projectId","name","groupKey","delivery"],"properties":{"bufferId":{"type":"string","description":"Unique buffer ID chosen by the caller (the dashboard uses `b_<timestamp>_<random>`)"},"customerId":{"type":"string","description":"Optional. Defaults to the authenticated customer; any other value is rejected with 403."},"projectId":{"type":"string"},"name":{"type":"string"},"enabled":{"type":"boolean","default":true},"groupKey":{"type":"object","required":["mode"],"description":"How the group key is extracted. With `jsonpath`, `path` is read from the JSON body\n(dot notation, e.g. `$.data.customer`); `fallbackHeader` is used when the path is\nmissing (or always, with `header` mode). Ingest fails with 400 if no key is found.\n","properties":{"mode":{"type":"string","enum":["jsonpath","header"]},"path":{"type":"string"},"fallbackHeader":{"type":"string"}}},"batch":{"type":"object","description":"Flush conditions. A group is delivered when any of them is met.","properties":{"maxItems":{"type":"integer","minimum":1,"default":50},"maxBytes":{"type":"integer","minimum":1,"default":262144},"idleTimeoutMs":{"type":"integer","minimum":100,"default":5000},"maxWaitFromFirstMs":{"type":"integer","minimum":100,"default":15000}}},"overflow":{"type":"object","description":"Per-segment limits; past them, new items go to a new segment of the same group key.","properties":{"maxGroupItems":{"type":"integer","default":2000},"maxGroupBytes":{"type":"integer","default":5242880}}},"delivery":{"type":"object","required":["url"],"properties":{"url":{"type":"string","format":"uri"},"method":{"type":"string","enum":["POST","PUT"],"default":"POST"},"headers":{"type":"object","additionalProperties":{"type":"string"},"description":"Extra headers sent with every delivery"},"idempotencyHeader":{"type":"string","default":"x-idempotency-key","description":"Header carrying a per-batch idempotency key (`grp:<groupId>:v<version>`)"}}},"throughput":{"type":"object","properties":{"maxAttempts":{"type":"integer","minimum":1,"maximum":5,"default":3,"description":"Delivery attempts (including the first) before the batch goes to the DLQ"},"backoff":{"type":"object","properties":{"initialMs":{"type":"integer","default":1000},"maxMs":{"type":"integer","default":60000},"multiplier":{"type":"number","default":2}}}}},"dlqPauseThreshold":{"type":"integer","minimum":1,"maximum":10,"default":3,"description":"The buffer is disabled after this many consecutive batches go to the DLQ"},"sourceVerification":{"type":"object","description":"Optional webhook source signature verification config (HMAC)","properties":{"enabled":{"type":"boolean","description":"Whether to verify incoming webhook signatures"},"signingSecret":{"type":"string","description":"Secret key used to compute HMAC signature"},"signatureHeader":{"type":"string","default":"x-webhook-signature","description":"HTTP header that carries the signature, as the lowercase hex HMAC-SHA256 of the JSON body (no prefix)"},"algorithm":{"type":"string","enum":["hmac-sha256","hmac-sha1"],"description":"Stored with the buffer; signatures are currently always verified with HMAC-SHA256"}}}}}}}},"responses":{"201":{"description":"Buffer created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Buffer"}}}},"400":{"description":"Missing fields, invalid config, or project not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Buffer already exists","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"get":{"tags":["Buffers"],"summary":"List buffers","description":"Returns all buffers for a customer, optionally filtered by project.","operationId":"listBuffers","security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"customerId","in":"query","required":false,"schema":{"type":"string"},"description":"The customer ID"},{"name":"projectId","in":"query","required":false,"schema":{"type":"string"},"description":"Filter by project ID"}],"responses":{"200":{"description":"List of buffers","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/Buffer"}}}}},"400":{"description":"Missing customerId","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/admin/buffers/{bufferId}":{"get":{"tags":["Buffers"],"summary":"Get a buffer","description":"Returns a single buffer by ID with its full configuration.","operationId":"getBuffer","security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"bufferId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Buffer details","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Buffer"}}}},"404":{"description":"Buffer not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"patch":{"tags":["Buffers"],"summary":"Update a buffer","description":"Updates a buffer's configuration. Only specific fields can be updated:\nname, enabled, groupKey, batch, overflow, delivery, sourceVerification, throughput, analytics, dlqPauseThreshold.\nRe-enabling a disabled buffer resets its DLQ count.\n","operationId":"updateBuffer","security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"bufferId","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string"},"enabled":{"type":"boolean"},"groupKey":{"type":"object"},"batch":{"type":"object"},"overflow":{"type":"object"},"delivery":{"type":"object"},"sourceVerification":{"type":"object","description":"Optional webhook source signature verification config (HMAC)","properties":{"enabled":{"type":"boolean","description":"Whether to verify incoming webhook signatures"},"signingSecret":{"type":"string","description":"Secret key used to compute HMAC signature"},"signatureHeader":{"type":"string","default":"x-webhook-signature","description":"HTTP header that carries the signature, as the lowercase hex HMAC-SHA256 of the JSON body (no prefix)"},"algorithm":{"type":"string","enum":["hmac-sha256","hmac-sha1"],"description":"Stored with the buffer; signatures are currently always verified with HMAC-SHA256"}}},"throughput":{"type":"object"},"analytics":{"type":"object"},"dlqPauseThreshold":{"type":"integer"}}}}}},"responses":{"200":{"description":"Updated buffer","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Buffer"}}}},"404":{"description":"Buffer not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"delete":{"tags":["Buffers"],"summary":"Delete a buffer","description":"Permanently deletes a buffer.","operationId":"deleteBuffer","security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"bufferId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"204":{"description":"Buffer deleted"},"404":{"description":"Buffer not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/admin/buffers/{bufferId}/clone":{"post":{"tags":["Buffers"],"summary":"Clone a buffer","description":"Creates a copy of an existing buffer with a new ID. The cloned buffer\ninherits all configuration but starts with fresh runtime state.\n","operationId":"cloneBuffer","security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"bufferId","in":"path","required":true,"schema":{"type":"string"},"description":"The buffer ID to clone"}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","description":"Name for the cloned buffer (defaults to \"Original Name (Copy)\")"}}}}}},"responses":{"201":{"description":"Cloned buffer","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Buffer"}}}},"400":{"description":"Failed to clone buffer configuration","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Buffer not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/admin/buffers/{bufferId}/test":{"post":{"tags":["Buffers"],"summary":"Test buffer delivery","description":"Sends a test delivery to the buffer's configured destination URL.\nUseful for verifying that the destination is reachable and configured correctly.\n","operationId":"testBuffer","security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"bufferId","in":"path","required":true,"schema":{"type":"string"},"description":"The buffer ID to test"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["payload"],"properties":{"payload":{"type":"object","description":"Test payload to deliver"}}}}}},"responses":{"200":{"description":"Test result","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"statusCode":{"type":"integer"},"durationMs":{"type":"integer"},"responseBody":{"type":"string","nullable":true},"error":{"type":"string","nullable":true}}}}}},"400":{"description":"Missing payload","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Buffer not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/analytics/summary":{"get":{"tags":["Analytics"],"summary":"Get dashboard summary stats","description":"Returns today's metrics, trend deltas compared to yesterday,\nand resource counts (projects and buffers).\n","operationId":"getDashboardSummary","security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"customerId","in":"query","required":false,"schema":{"type":"string"},"description":"The customer ID to get stats for"}],"responses":{"200":{"description":"Dashboard summary stats","content":{"application/json":{"schema":{"type":"object","properties":{"today":{"type":"object","properties":{"ingestCount":{"type":"integer"},"deliveryCount":{"type":"integer"},"deliverySuccess":{"type":"integer"},"deliveryFail":{"type":"integer"},"dlqCount":{"type":"integer"},"avgBatchSize":{"type":"number"},"compressionRatio":{"type":"number"}}},"trend":{"type":"object","properties":{"ingestDelta":{"type":"number","description":"Percentage change from yesterday"},"deliveryDelta":{"type":"number"},"successRateDelta":{"type":"number"}}},"counts":{"type":"object","properties":{"projects":{"type":"integer"},"buffers":{"type":"integer"}}}}}}}},"400":{"description":"Missing customerId","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/analytics/timeseries":{"get":{"tags":["Analytics"],"summary":"Get analytics timeseries data","description":"Returns time series metrics, latency histograms, and aggregated totals.\nCan be filtered by project and buffer. Defaults to the last 7 days.\n","operationId":"getAnalyticsTimeseries","security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"customerId","in":"query","required":false,"schema":{"type":"string"},"description":"The customer ID"},{"name":"projectId","in":"query","required":false,"schema":{"type":"string"},"description":"Filter by project ID"},{"name":"bufferId","in":"query","required":false,"schema":{"type":"string"},"description":"Filter by buffer ID"},{"name":"days","in":"query","required":false,"schema":{"type":"integer","default":7},"description":"Number of days to include"}],"responses":{"200":{"description":"Analytics timeseries data","content":{"application/json":{"schema":{"type":"object","properties":{"timeseries":{"type":"array","items":{"type":"object","properties":{"date":{"type":"string"},"ingestCount":{"type":"integer"},"deliveryCount":{"type":"integer"},"deliverySuccess":{"type":"integer"},"deliveryFail":{"type":"integer"},"retryCount":{"type":"integer"},"itemsDelivered":{"type":"integer"}}}},"histograms":{"type":"object","properties":{"timeToFlush":{"type":"object","additionalProperties":{"type":"integer"}},"deliveryDuration":{"type":"object","additionalProperties":{"type":"integer"}}}},"totals":{"type":"object","properties":{"ingestCount":{"type":"integer"},"deliveryCount":{"type":"integer"},"deliverySuccess":{"type":"integer"},"deliveryFail":{"type":"integer"},"retryCount":{"type":"integer"},"itemsDelivered":{"type":"integer"},"dlqCount":{"type":"integer"},"compressionRatio":{"type":"number"},"successRate":{"type":"number"}}}}}}}},"400":{"description":"Missing customerId","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/admin/api-keys":{"post":{"tags":["API Keys"],"summary":"Create a new API key","description":"Creates a new API key for the authenticated customer. The plaintext\nsecret is returned only once in the response — store it securely.\nMaximum of 10 active keys per customer.\n","operationId":"createApiKey","security":[{"ApiKeyAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["name"],"properties":{"name":{"type":"string","maxLength":100,"description":"Human-readable name for the key"},"role":{"type":"string","enum":["viewer","member","admin","owner"],"description":"Role for the key (defaults to caller's role, cannot exceed it)"},"projectIds":{"type":"array","items":{"type":"string"},"description":"Restrict key to specific projects"}}}}}},"responses":{"201":{"description":"API key created (secret returned only once)","content":{"application/json":{"schema":{"type":"object","properties":{"keyId":{"type":"string"},"name":{"type":"string"},"secret":{"type":"string","description":"Plaintext API key (wbk_ prefix). Not retrievable again."},"keyPrefix":{"type":"string"},"role":{"type":"string"},"projectIds":{"type":"array","items":{"type":"string"}},"createdAt":{"type":"string","format":"date-time"}}}}}},"400":{"description":"Invalid input","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Cannot create key with higher role than own","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Maximum active keys limit reached","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"get":{"tags":["API Keys"],"summary":"List API keys","description":"Lists all API keys for the authenticated customer.\nNever returns the key hash or secret.\n","operationId":"listApiKeys","security":[{"ApiKeyAuth":[]}],"responses":{"200":{"description":"List of API keys","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"keyId":{"type":"string"},"customerId":{"type":"string"},"name":{"type":"string"},"keyPrefix":{"type":"string","description":"First 8 characters of the key for identification"},"role":{"type":"string"},"projectIds":{"type":"array","items":{"type":"string"}},"createdAt":{"type":"string","format":"date-time"},"revokedAt":{"type":"string","format":"date-time","nullable":true}}}}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/admin/api-keys/{keyId}":{"delete":{"tags":["API Keys"],"summary":"Revoke an API key","description":"Soft-deletes an API key by setting a revokedAt timestamp. The key can no longer be used for authentication.","operationId":"revokeApiKey","security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"keyId","in":"path","required":true,"schema":{"type":"string"},"description":"The API key ID to revoke"}],"responses":{"204":{"description":"API key revoked"},"404":{"description":"API key not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"API key is already revoked","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/admin/credit-packs":{"get":{"tags":["Credits"],"summary":"List credit packs","description":"Returns the one-time credit packs with prices derived from the subscription plans: each pack uses the per-credit rate of the largest plan whose monthly credits are less than or equal to the pack size, using the plans' prices in the requested currency. `available` is false until the pack's Hubla offer is configured. If the plans have no prices in the requested currency yet, the packs are priced in the first currency of `currencies` and `currency` says which one.\n","operationId":"listCreditPacks","security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"currency","in":"query","required":false,"schema":{"type":"string","enum":["BRL","USD"],"default":"BRL"},"description":"Currency to price the packs in"}],"responses":{"200":{"description":"Credit packs","content":{"application/json":{"schema":{"type":"object","properties":{"currency":{"type":"string","description":"Currency of every price in `packs`"},"currencies":{"type":"array","items":{"type":"string"},"description":"Currencies the packs can be priced in"},"packs":{"type":"array","items":{"type":"object","properties":{"packId":{"type":"string"},"credits":{"type":"integer"},"priceInCents":{"type":"integer"},"currency":{"type":"string"},"pricePer1kCents":{"type":"number","description":"Price per 1,000 credits in cents (may be fractional)"},"tier":{"type":"object","description":"The plan whose per-credit rate applies","properties":{"planId":{"type":"string"},"name":{"type":"string"},"priceInCents":{"type":"integer"},"creditsPerCycle":{"type":"integer"}}},"available":{"type":"boolean"},"betterValue":{"type":"object","nullable":true,"description":"A bigger pack that costs the same or less","properties":{"packId":{"type":"string"},"credits":{"type":"integer"},"priceInCents":{"type":"integer"}}}}}}}}}}},"400":{"description":"Unsupported currency (code `unsupported_currency`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/admin/credit-packs/{packId}/checkout-url":{"get":{"tags":["Credits"],"summary":"Get credit pack checkout URL","description":"Returns the Hubla checkout URL for a credit pack, linked to the authenticated customer. Credit packs can only be bought with an active subscription; otherwise the request fails with 403 and code `subscription_required`. Purchased credits never expire. If the amount paid differs from the pack price (e.g. a coupon), the credits granted are proportional to the amount paid.\n","operationId":"getCreditPackCheckoutUrl","security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"packId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Checkout URL generated","content":{"application/json":{"schema":{"type":"object","properties":{"checkoutUrl":{"type":"string","format":"uri"},"packId":{"type":"string"},"credits":{"type":"integer"}}}}}},"403":{"description":"An active subscription is required to buy credit packs (code `subscription_required`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Pack not found or inactive","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Pack is not available for purchase yet (no Hubla offer configured)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"Payment provider not configured","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/admin/credits":{"get":{"tags":["Credits"],"summary":"Get credit balance","description":"Returns the current credit balance, limit, and usage for the authenticated customer.","operationId":"getCredits","security":[{"ApiKeyAuth":[]}],"responses":{"200":{"description":"Credit balance information","content":{"application/json":{"schema":{"type":"object","properties":{"customerId":{"type":"string"},"credits":{"type":"number","description":"Current credit balance"},"creditLimit":{"type":"number","description":"Monthly credit limit"},"creditsUsedThisMonth":{"type":"number","description":"Credits consumed this billing cycle"},"lastResetAt":{"type":"number","description":"Timestamp of last monthly reset"}}}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/admin/credits/add":{"post":{"tags":["Credits"],"summary":"Add credits (local emulator only)","description":"Development helper that grants free credits to the authenticated customer (maximum 100,000 per request). Only available when the API runs in the local emulator; everywhere else it responds 404. Buy credits with a credit pack instead (see /v1/admin/credit-packs).\n","operationId":"addCredits","security":[{"ApiKeyAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["amount"],"properties":{"amount":{"type":"number","minimum":1,"maximum":100000,"description":"Number of credits to add"}}}}}},"responses":{"200":{"description":"Credits added","content":{"application/json":{"schema":{"type":"object","properties":{"customerId":{"type":"string"},"creditsAdded":{"type":"number"},"newBalance":{"type":"number"}}}}}},"400":{"description":"Invalid amount","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not available outside the local emulator","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/admin/credits/transactions":{"get":{"tags":["Credits"],"summary":"Get credit transaction history","description":"Returns a list of credit transactions (purchases, consumptions, refunds) for the authenticated customer.","operationId":"getCreditTransactions","security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"limit","in":"query","required":false,"schema":{"type":"integer","default":50},"description":"Maximum number of transactions to return"}],"responses":{"200":{"description":"Credit transaction list","content":{"application/json":{"schema":{"type":"object","properties":{"transactions":{"type":"array","items":{"type":"object","properties":{"transactionId":{"type":"string"},"customerId":{"type":"string"},"amount":{"type":"number","description":"Positive for grants, negative for consumption"},"type":{"type":"string","enum":["grant","purchase","consumption","refund"]},"description":{"type":"string"},"balanceAfter":{"type":"number"},"createdAt":{"type":"number"}}}}}}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/dlq":{"get":{"tags":["DLQ"],"summary":"List dead letter queue items","description":"Returns DLQ items for the customer. Optionally filter by buffer ID.\nResults are limited to a maximum of 500 items.\n","operationId":"listDlqItems","security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"customerId","in":"query","required":false,"schema":{"type":"string"},"description":"The customer ID"},{"name":"bufferId","in":"query","required":false,"schema":{"type":"string"},"description":"Filter by buffer ID"},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","default":100,"maximum":500},"description":"Maximum number of items to return"}],"responses":{"200":{"description":"List of DLQ items","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/DlqItem"}}}}},"400":{"description":"Missing customerId","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Buffer not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/dlq/{dlqId}":{"get":{"tags":["DLQ"],"summary":"Get DLQ item details","description":"Returns the full details of a single dead letter queue item.","operationId":"getDlqItem","security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"dlqId","in":"path","required":true,"schema":{"type":"string"},"description":"The DLQ item ID"},{"name":"customerId","in":"query","required":false,"schema":{"type":"string"},"description":"The customer ID (for ownership validation)"}],"responses":{"200":{"description":"DLQ item details","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DlqItem"}}}},"400":{"description":"Missing customerId","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Access denied","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"DLQ item not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"delete":{"tags":["DLQ"],"summary":"Delete a DLQ item","description":"Permanently deletes a dead letter queue item and its stored batch.","operationId":"deleteDlqItem","security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"dlqId","in":"path","required":true,"schema":{"type":"string"},"description":"The DLQ item ID to delete"},{"name":"customerId","in":"query","required":false,"schema":{"type":"string"},"description":"The customer ID (for ownership validation)"}],"responses":{"204":{"description":"DLQ item deleted"},"400":{"description":"Missing customerId","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Access denied","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"DLQ item not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/dlq/{dlqId}/batch":{"get":{"tags":["DLQ"],"summary":"Get the stored batch of a DLQ item","description":"Returns the exact delivery body (`{webbu, window, items[]}`) that failed and that\n`POST /v1/dlq/{dlqId}/retry` sends again. Entries created before batches were\nstored have no batch and return `409` with code `payload_unavailable`.\n","operationId":"getDlqBatch","security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"dlqId","in":"path","required":true,"schema":{"type":"string"},"description":"The DLQ item ID"},{"name":"customerId","in":"query","required":false,"schema":{"type":"string"},"description":"The customer ID (for ownership validation)"}],"responses":{"200":{"description":"The stored delivery body","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeliveryBatch"}}}},"403":{"description":"Access denied","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"DLQ item not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"The batch of this entry was not stored (`code` is `payload_unavailable`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/dlq/{dlqId}/retry":{"post":{"tags":["DLQ"],"summary":"Retry a DLQ item","description":"Redelivers the stored batch of a dead letter queue item — the same events, in the\nsame order, with the same body and idempotency key (`grp:{groupId}:v{version}`) —\nto the buffer's **current** delivery configuration (URL, method, headers, signing).\nThis is a single, synchronous delivery attempt (destination timeout: 10 seconds).\n\n- Delivered (`200`): the DLQ item and its stored batch are deleted.\n- Destination failed (`502`, code `delivery_failed`): the item stays in the DLQ with\n  `retryCount`, `lastError` and `lastStatusCode` updated. You can retry it again.\n- Another retry of the same item is in flight (`409`, code `retry_in_progress`).\n- The item was created before batches were stored (`409`, code `payload_unavailable`).\n","operationId":"retryDlqItem","security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"dlqId","in":"path","required":true,"schema":{"type":"string"},"description":"The DLQ item ID to retry"}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"customerId":{"type":"string","description":"The customer ID (optional; must match the authenticated customer)"}}}}}},"responses":{"200":{"description":"Batch redelivered; the DLQ item was removed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DlqRetryResult"}}}},"403":{"description":"Access denied","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"DLQ item or buffer not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Not retried: `retry_in_progress` (another retry of this item is running) or\n`payload_unavailable` (the item's events were not stored)\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"502":{"description":"The destination did not accept the batch; the item stays in the DLQ","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DlqRetryFailure"}}}}}}},"/v1/ingest/{projectId}/{bufferId}":{"post":{"tags":["Ingest"],"summary":"Ingest a webhook payload","description":"Receives a webhook payload, extracts the group key, and buffers it\nfor batched delivery. Returns 202 Accepted immediately.\n\nThe group key is read from the JSON body at the buffer's `groupKey.path`\n(`jsonpath` mode), falling back to the `groupKey.fallbackHeader` request\nheader. If no key is found the request fails with 400.\n\nAn API key is optional; if `X-API-Key` is sent it must be valid and have\naccess to the buffer. Each request uses 1 credit per started KB of the\nJSON-serialized payload (429 when the balance is insufficient).\n","operationId":"ingestWebhook","security":[{},{"ApiKeyAuth":[]}],"parameters":[{"name":"projectId","in":"path","required":true,"schema":{"type":"string"},"description":"The project ID"},{"name":"bufferId","in":"path","required":true,"schema":{"type":"string"},"description":"The buffer ID"},{"name":"X-Idempotency-Key","in":"header","required":false,"schema":{"type":"string"},"description":"Message ID used for deduplication when the JSON body has no top-level `messageId`\nfield. If neither is present, a random UUID is generated (no deduplication).\n"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","description":"Any valid JSON payload to buffer"}}}},"responses":{"202":{"description":"Webhook accepted and buffered","content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"string","example":"accepted"},"messageId":{"type":"string","description":"The message ID used for deduplication"},"itemId":{"type":"string","description":"The unique item ID assigned to this payload"}}}}}},"400":{"description":"Invalid request (buffer disabled, project mismatch, or missing group key)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Invalid or revoked API key, or missing/invalid source signature (when source verification is enabled)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"API key does not have access to this buffer","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Buffer or customer not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Insufficient credits","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","example":"Insufficient credits"},"credits":{"type":"number","description":"Current credit balance"},"required":{"type":"number","description":"Credits required for this request"},"message":{"type":"string"}}}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/admin/plans":{"get":{"tags":["Payments"],"summary":"List available subscription plans","description":"Returns all available subscription plans.","operationId":"listPlans","security":[{"ApiKeyAuth":[]}],"responses":{"200":{"description":"List of plans","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/Plan"}}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/admin/subscription":{"get":{"tags":["Payments"],"summary":"Get current subscription","description":"Returns the authenticated customer's current subscription and associated plan details.","operationId":"getSubscription","security":[{"ApiKeyAuth":[]}],"responses":{"200":{"description":"Subscription and plan details (null if no subscription)","content":{"application/json":{"schema":{"type":"object","nullable":true,"properties":{"subscription":{"$ref":"#/components/schemas/Subscription"},"plan":{"$ref":"#/components/schemas/Plan"}}}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"delete":{"tags":["Payments"],"summary":"Cancel subscription (graceful)","description":"Schedules cancellation for end of current billing period.\nThe subscription remains active (status: CANCELLING) until currentPeriodEnd.\n","operationId":"cancelSubscription","security":[{"ApiKeyAuth":[]}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"reason":{"type":"string","description":"Reason for cancellation"}}}}}},"responses":{"200":{"description":"Cancellation scheduled","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string"},"endsAt":{"type":"number","description":"Timestamp when subscription ends"},"status":{"type":"string","example":"CANCELLING"}}}}}},"404":{"description":"No active subscription","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Already cancelling or cancelled","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"Payment provider not configured","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/admin/subscription/immediate":{"delete":{"tags":["Payments"],"summary":"Cancel subscription immediately","description":"Terminates the subscription immediately (admin/owner only).\nBypasses graceful end-of-period cancellation.\n","operationId":"cancelSubscriptionImmediate","security":[{"ApiKeyAuth":[]}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"reason":{"type":"string","description":"Reason for immediate cancellation"}}}}}},"responses":{"204":{"description":"Subscription cancelled immediately"},"404":{"description":"No active subscription","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Already cancelled","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"Payment provider not configured","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/admin/invoices":{"get":{"tags":["Payments"],"summary":"List invoices","description":"Returns a list of invoices for the authenticated customer.","operationId":"listInvoices","security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"limit","in":"query","required":false,"schema":{"type":"integer","default":50},"description":"Maximum number of invoices to return"}],"responses":{"200":{"description":"List of invoices","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/Invoice"}}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/admin/invoices/{invoiceId}":{"get":{"tags":["Payments"],"summary":"Get invoice details","description":"Returns the details of a specific invoice.","operationId":"getInvoice","security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"invoiceId","in":"path","required":true,"schema":{"type":"string"},"description":"The invoice ID"}],"responses":{"200":{"description":"Invoice details","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Invoice"}}}},"403":{"description":"Forbidden (invoice belongs to another customer)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Invoice not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/admin/subscription/checkout-url":{"get":{"tags":["Payments"],"summary":"Get checkout URL","description":"Returns a checkout URL for the specified plan, linked to the authenticated customer.","operationId":"getCheckoutUrl","security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"planId","in":"query","required":true,"schema":{"type":"string"},"description":"The plan ID to get a checkout URL for"}],"responses":{"200":{"description":"Checkout URL generated","content":{"application/json":{"schema":{"type":"object","properties":{"checkoutUrl":{"type":"string","format":"uri"},"planId":{"type":"string"},"planName":{"type":"string"}}}}}},"400":{"description":"Missing planId or plan inactive","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Plan not found or missing checkout URL","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"Payment provider not configured","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/health":{"get":{"tags":["Health"],"summary":"Health check","description":"Returns the service health status.","operationId":"healthCheck","security":[],"responses":{"200":{"description":"Service is healthy","content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"string","example":"ok"}}}}}}}}}}}