{
  "openapi": "3.1.0",
  "info": {
    "title": "Recoup API - Accounts",
    "description": "API documentation for the Recoup platform - an AI agent platform for the music industry",
    "license": {
      "name": "MIT"
    },
    "version": "1.0.0"
  },
  "servers": [
    {
      "url": "https://api.recoupable.dev"
    }
  ],
  "paths": {
    "/api/accounts/{id}": {
      "get": {
        "description": "Retrieve detailed account information by ID. Returns the account with associated profile info, emails, and wallet addresses.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "The unique identifier (UUID) of the account",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Account retrieved successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GetAccountResponse"
                }
              }
            }
          },
          "404": {
            "description": "Account not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AccountErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/accounts/id": {
      "get": {
        "description": "Retrieve the ID of the authenticated account associated with the provided credentials. This is useful when you have an API key or access token but do not yet know the corresponding accountId.",
        "parameters": [],
        "security": [
          {
            "apiKeyAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Account ID retrieved successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GetAccountIdResponse"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized - missing or invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AccountErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/accounts": {
      "post": {
        "description": "Create a new account or retrieve an existing account by email or wallet address. If an account with the provided email or wallet already exists, returns that account. Otherwise creates a new account and initializes credits.",
        "requestBody": {
          "description": "Account credentials to create or lookup",
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateAccountRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Account created or retrieved successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AccountDataResponse"
                }
              }
            }
          },
          "400": {
            "description": "Bad request - failed to create account",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AccountErrorResponse"
                }
              }
            }
          }
        }
      },
      "patch": {
        "description": "Update an existing account's profile information including name, organization, image, instruction, job title, role type, company name, and knowledges. Requires authentication via x-api-key or Authorization Bearer token. The authenticated account may update itself or a permitted account_id override.",
        "requestBody": {
          "description": "Account fields to update",
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UpdateAccountRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Account updated successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AccountDataResponse"
                }
              }
            }
          },
          "400": {
            "description": "Bad request - account not found or update failed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AccountErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized - missing or invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AccountErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden - account override not permitted",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AccountErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/accounts/artists": {
      "post": {
        "description": "Add an artist to an account's list of associated artists. If the artist is already associated with the account, returns success without modification.",
        "requestBody": {
          "description": "Account and artist identifiers",
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AddArtistToAccountRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Artist added to account successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AddArtistSuccessResponse"
                }
              }
            }
          },
          "400": {
            "description": "Bad request - account not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AccountErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/organizations": {
      "get": {
        "description": "Retrieve all organizations that the authenticated account belongs to. Pass account_id to retrieve organizations for a specific account the API key has access to.",
        "security": [
          {
            "apiKeyAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "account_id",
            "in": "query",
            "description": "Filter to a specific account. Only applicable when the authenticated account has access to multiple accounts via organization membership.",
            "required": false,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Organizations retrieved successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GetOrganizationsResponse"
                }
              }
            }
          },
          "400": {
            "description": "Bad request - invalid query parameters",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OrganizationsErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized - invalid or missing API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden - account_id is not a member of the organization or account tried to filter by an account_id they don't have access to",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "post": {
        "description": "Create a new organization. The creator is automatically added as a member of the organization.",
        "security": [
          {
            "apiKeyAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "description": "Organization creation parameters",
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateOrganizationRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Organization created successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CreateOrganizationResponse"
                }
              }
            }
          },
          "400": {
            "description": "Bad request - missing required parameters",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OrganizationsErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/organizations/artists": {
      "post": {
        "description": "Add an artist to an organization. This allows organization members to access and manage the artist. This endpoint is idempotent - calling it multiple times with the same artistId and organizationId will not create duplicate records.",
        "security": [
          {
            "apiKeyAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "description": "Artist-organization association parameters",
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AddArtistToOrganizationRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Artist added to organization successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AddArtistToOrganizationResponse"
                }
              }
            }
          },
          "400": {
            "description": "Bad request - missing required parameters",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OrganizationsErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OrganizationsErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Caller is not a member of the organization",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OrganizationsErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/organizations/members": {
      "post": {
        "description": "Add a member to an organization. This endpoint is idempotent - Adding an existing member returns the existing membership.",
        "security": [
          {
            "apiKeyAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "description": "Member-organization association parameters",
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AddOrganizationMemberRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Member added to organization successfully (or already a member)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AddOrganizationMemberResponse"
                }
              }
            }
          },
          "400": {
            "description": "Bad request - invalid or missing parameters",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized - missing or invalid authentication",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden - caller is not a member of the organization",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        }
      },
      "delete": {
        "description": "Remove a member from an organization. This endpoint is idempotent - Removing an account that is not a member succeeds without error.",
        "security": [
          {
            "apiKeyAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "organization_id",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "The account ID of the organization"
          },
          {
            "name": "account_id",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "The account ID of the member to remove"
          }
        ],
        "responses": {
          "200": {
            "description": "Member removed from organization (or was not a member)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RemoveOrganizationMemberResponse"
                }
              }
            }
          },
          "400": {
            "description": "Bad request - invalid or missing parameters",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized - missing or invalid authentication",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden - caller is not a member of the organization",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        }
      }
    },
    "/api/organizations/domains": {
      "get": {
        "description": "List the email domains mapped to an organization. Accounts that sign up with an email at a mapped domain automatically join the organization.",
        "security": [
          {
            "apiKeyAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "organization_id",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "The account ID of the organization"
          }
        ],
        "responses": {
          "200": {
            "description": "Domains retrieved successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GetOrganizationDomainsResponse"
                }
              }
            }
          },
          "400": {
            "description": "Bad request - invalid or missing parameters",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized - missing or invalid authentication",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden - caller is not a member of the organization",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        }
      },
      "post": {
        "description": "Map an email domain to an organization for automatic membership. After mapping, any account that signs up (or signs in) with an email at this domain is automatically added to the organization. A domain can belong to at most one organization.  This endpoint is idempotent - re-adding the same domain to the same organization returns the existing mapping.",
        "security": [
          {
            "apiKeyAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "description": "Domain-organization mapping parameters",
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AddOrganizationDomainRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Domain mapped to organization successfully (or already mapped)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AddOrganizationDomainResponse"
                }
              }
            }
          },
          "400": {
            "description": "Bad request - invalid or missing parameters",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized - missing or invalid authentication",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden - caller is not a member of the organization",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "409": {
            "description": "Conflict - domain is already mapped to a different organization",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        }
      },
      "delete": {
        "description": "Remove an email domain mapping from an organization. New signups at this domain will no longer auto-join the organization; existing members are not affected. This endpoint is idempotent.",
        "security": [
          {
            "apiKeyAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "organization_id",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "The account ID of the organization"
          },
          {
            "name": "domain",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "The email domain to unmap (e.g. \"seekermusic.com\")"
          }
        ],
        "responses": {
          "200": {
            "description": "Domain mapping removed (or did not exist)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RemoveOrganizationDomainResponse"
                }
              }
            }
          },
          "400": {
            "description": "Bad request - invalid or missing parameters",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized - missing or invalid authentication",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden - caller is not a member of the organization",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        }
      }
    },
    "/api/subscriptions/sessions": {
      "post": {
        "description": "Create a checkout session to start a subscription for the authenticated account. Returns a hosted checkout URL that the client should redirect to. `plan` picks the price: `pro` (default) is pre-configured with a 30-day trial period; `starter` is charged at checkout with no trial.",
        "requestBody": {
          "description": "Session creation parameters",
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateSubscriptionSessionRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Checkout session created successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CreateSubscriptionSessionResponse"
                }
              }
            }
          },
          "400": {
            "description": "Bad request - missing or invalid parameters, or `plan` is `starter` while the Starter price is not configured (`error` is literally `starter_unavailable`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SubscriptionSessionErrorResponse"
                },
                "example": {
                  "error": "starter_unavailable"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized - an auth header was supplied but is invalid (omit it entirely for an anonymous session)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SubscriptionSessionErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Internal server error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SubscriptionSessionErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/accounts/{id}/subscription": {
      "get": {
        "description": "Retrieve the subscription that confers an account's plan, directly or via an organization: `isPro`, `status`, `plan`, `source`, plus the billing fields `name`, `amountCents`, `currency`, `interval`, `collectionMethod` and `currentPeriodEnd`. Everything but `isPro` and `status` is null when `status` is `none`. `id` may be the authenticated account or an organization the caller belongs to.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "The unique identifier (UUID) of the account. Must be the authenticated account or another accessible via organization membership.",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "security": [
          {
            "apiKeyAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Subscription retrieved successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SubscriptionResponse"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized - invalid or missing authentication",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SubscriptionErrorResponse"
                },
                "example": {
                  "error": "Unauthorized"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden - account not accessible to the authenticated account",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SubscriptionErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Account not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SubscriptionErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/accounts/{id}/credits": {
      "get": {
        "description": "Retrieve the current credit balance for an account. Returns the remaining credits along with the plan-derived monthly total and used count, plus a flag indicating whether the account is on a pro plan (directly or via an organization). Credits refill monthly; the `timestamp` field reflects the last refill or balance update.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "The unique identifier (UUID) of the account. Must be the authenticated account or another accessible via organization membership.",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "security": [
          {
            "apiKeyAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Credit balance retrieved successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AccountCreditsResponse"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized - invalid or missing authentication",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AccountCreditsErrorResponse"
                },
                "example": {
                  "error": "Unauthorized"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden - account not accessible to the authenticated account",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AccountCreditsErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/accounts/{id}/usage": {
      "get": {
        "description": "List the charges that consumed an account's credits: one line item per deduction from `usage_events`, newest first, over a period. Each item carries the amount as the raw ledger integer (`credits_deducted`, micro-dollars: 1,000,000 = $1.00) and the same amount formatted as a dollar string (`usd`), and the response carries the total for the whole period, not just the page. Access is the same as [`GET /api/accounts/{id}/credits`](/api-reference/accounts/credits-get): the authenticated account itself, or another account reachable through organization membership.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "The unique identifier (UUID) of the account. Must be the authenticated account or another accessible via organization membership.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Maximum number of line items to return per page.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 20
            }
          },
          {
            "name": "sort",
            "in": "query",
            "required": false,
            "description": "Order of the line items, both descending: `created_at` (newest first, the default) or `cost` (largest `credits_deducted` first, ties by newest).",
            "schema": {
              "type": "string",
              "enum": [
                "created_at",
                "cost"
              ],
              "default": "created_at"
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "required": false,
            "description": "Opaque paging token: pass the `next_cursor` value from the previous page, with the same `sort`, `from` and `to`. With `sort=created_at` it is the last item's `created_at`; with `sort=cost` it encodes the last item's amount and id. Returns the items that follow it.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "from",
            "in": "query",
            "required": false,
            "description": "Start of the period (inclusive), ISO 8601. Defaults to the start of the current UTC month.",
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          },
          {
            "name": "to",
            "in": "query",
            "required": false,
            "description": "End of the period (exclusive), ISO 8601. Defaults to now.",
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          }
        ],
        "security": [
          {
            "apiKeyAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Usage line items retrieved successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AccountUsageResponse"
                }
              }
            }
          },
          "400": {
            "description": "Bad request - invalid query parameter (for example `limit` above 100 or a `cursor` that is not a timestamp)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AccountCreditsErrorResponse"
                },
                "example": {
                  "error": "limit must be between 1 and 100"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized - invalid or missing authentication",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AccountCreditsErrorResponse"
                },
                "example": {
                  "error": "Unauthorized"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden - account not accessible to the authenticated account",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AccountCreditsErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/accounts/{id}/payment-method": {
      "get": {
        "description": "Retrieve the default payment method on file for an account. Returns `card: null` when no payment method has been saved yet — the top-up dialog uses this to decide whether to show a pre-charge confirmation (card present) or route to a checkout session to collect one (`card: null`). Cards are returned even when expired; callers should compare `exp_month` / `exp_year` against the current date and warn the customer, since an off-session charge against an expired card will decline.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "The unique identifier (UUID) of the account. Must be the authenticated account or another accessible via organization membership.",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "security": [
          {
            "apiKeyAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Default payment method retrieved successfully (may be `null` if none on file).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AccountPaymentMethodResponse"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized - invalid or missing authentication",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AccountPaymentMethodErrorResponse"
                },
                "example": {
                  "error": "Unauthorized"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden - the account is not the caller's and not an organization they belong to (an unknown id also returns 403, never 404)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AccountPaymentMethodErrorResponse"
                }
              }
            }
          }
        }
      },
      "post": {
        "description": "Create a $0 card-on-file checkout session for an account. Stripe `setup` mode saves a payment method without charging anything or starting a subscription; the saved card becomes the account's default and is what credit purchases and auto top-up charge. The account's Stripe customer is created (and tagged with the account id) if it does not exist yet. Returns a hosted checkout URL that the client should redirect to. `id` may be the authenticated account or an organization the caller belongs to.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "The unique identifier (UUID) of the account. Must be the authenticated account or another accessible via organization membership.",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "security": [
          {
            "apiKeyAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "description": "Card-on-file session parameters",
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateCardOnFileSessionRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Card-on-file session created successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CreateSubscriptionSessionResponse"
                }
              }
            }
          },
          "400": {
            "description": "Bad request - missing or invalid parameters",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AccountPaymentMethodErrorResponse"
                },
                "example": {
                  "error": "successUrl must be a valid URL"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized - invalid or missing authentication",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AccountPaymentMethodErrorResponse"
                },
                "example": {
                  "error": "Unauthorized"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden - the account is not the caller's and not an organization they belong to (an unknown id also returns 403, never 404)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AccountPaymentMethodErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Internal server error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AccountPaymentMethodErrorResponse"
                },
                "example": {
                  "error": "Internal server error"
                }
              }
            }
          }
        }
      },
      "delete": {
        "description": "Detach the default card on file from an account. After this call `GET /api/accounts/{id}/payment-method` returns `card: null`, credit purchases route through checkout again, and auto top-up (if enabled) is turned off because it has nothing to charge. Subscriptions collected by invoice are unaffected. `id` may be the authenticated account or an organization the caller belongs to.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "The unique identifier (UUID) of the account. Must be the authenticated account or another accessible via organization membership.",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "security": [
          {
            "apiKeyAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "responses": {
          "204": {
            "description": "Payment method detached; no body."
          },
          "401": {
            "description": "Unauthorized - invalid or missing authentication",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AccountPaymentMethodErrorResponse"
                },
                "example": {
                  "error": "Unauthorized"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden - the account is not the caller's and not an organization they belong to (an unknown id also returns 403, never 404)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AccountPaymentMethodErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/accounts/{id}/portal": {
      "post": {
        "description": "Create a Stripe Customer Portal session for an account. Returns a hosted URL where the customer can update the card on file, view invoices, or cancel a Starter or Pro subscription; the client should redirect the user to that URL. `id` may be the authenticated account or an organization the caller belongs to. Enterprise (invoiced) plans are managed with your Recoup contact and the app does not offer this portal for them, but the endpoint works for any account with a Stripe customer.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "The unique identifier (UUID) of the account. Must be the authenticated account or another accessible via organization membership.",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "security": [
          {
            "apiKeyAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "description": "Portal session parameters",
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateSubscriptionPortalRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Portal session created successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CreateSubscriptionPortalResponse"
                }
              }
            }
          },
          "400": {
            "description": "Bad request - invalid parameters, or the account has no Stripe customer yet",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SubscriptionPortalErrorResponse"
                },
                "example": {
                  "error": "No active subscription found"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized - invalid or missing authentication",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SubscriptionPortalErrorResponse"
                },
                "example": {
                  "error": "Unauthorized"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden - the account is not the caller's and not an organization they belong to (an unknown id also returns 403, never 404)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SubscriptionPortalErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/accounts/{id}/payments": {
      "get": {
        "description": "List the invoices paid or owed by an account, newest first: subscription renewals, credit purchases, and invoiced enterprise plans alike. Non-draft rows carry the hosted invoice URL for the receipt; `url` is null for drafts. Returns an empty list (not 404) when the account has no Stripe customer or no invoices yet. `id` may be the authenticated account or an organization the caller belongs to.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "The unique identifier (UUID) of the account. Must be the authenticated account or another accessible via organization membership.",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "description": "Maximum rows to return.",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 20
            }
          },
          {
            "name": "startingAfter",
            "in": "query",
            "description": "Cursor for the next page: the `id` of the last payment from the previous response.",
            "required": false,
            "schema": {
              "type": "string"
            },
            "example": "in_1U5xj400JObOnOb5BE0CmxCt"
          }
        ],
        "security": [
          {
            "apiKeyAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Payments retrieved successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AccountPaymentsResponse"
                }
              }
            }
          },
          "400": {
            "description": "Bad request - invalid `id`, `limit`, or `startingAfter`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AccountPaymentsErrorResponse"
                },
                "example": {
                  "error": "limit must be between 1 and 100"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized - invalid or missing authentication",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AccountPaymentsErrorResponse"
                },
                "example": {
                  "error": "Unauthorized"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden - the account is not the caller's and not an organization they belong to (an unknown id also returns 403, never 404)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AccountPaymentsErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/accounts/{id}/auto-top-up": {
      "get": {
        "description": "Read the auto top-up settings for an account. Auto top-up is opt-in: it is off for every account until the account turns it on and chooses both the amount to buy and the balance that triggers it. When it is on, the api charges the default card on file for `amountCents` the first time a credit deduction leaves the balance below `thresholdCents`, then grants the credits and emails a receipt. `id` may be the authenticated account or an organization the caller belongs to.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "The unique identifier (UUID) of the account. Must be the authenticated account or another accessible via organization membership.",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "security": [
          {
            "apiKeyAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Auto top-up settings (defaults when never configured).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AutoTopUpResponse"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized - invalid or missing authentication",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AutoTopUpErrorResponse"
                },
                "example": {
                  "error": "Unauthorized"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden - the account is not the caller's and not an organization they belong to (an unknown id also returns 403, never 404)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AutoTopUpErrorResponse"
                }
              }
            }
          }
        }
      },
      "put": {
        "description": "Set the auto top-up settings for an account. All three fields are required on every call; there is no partial update. Turning it on requires a card on file (`GET /api/accounts/{id}/payment-method` returns a card), an amount between 5.00 and 1,000.00 USD, and a threshold below the amount. Turning it off keeps the last amount and threshold so the account can turn it back on without retyping them. Guardrails that apply once it is on: at most one top-up per account per 10 minutes; a card decline turns `enabled` back to `false`, records `lastError`, and emails the account instead of retrying; removing the card turns it off. `id` may be the authenticated account or an organization the caller belongs to.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "The unique identifier (UUID) of the account. Must be the authenticated account or another accessible via organization membership.",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "security": [
          {
            "apiKeyAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "description": "Auto top-up settings",
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UpdateAutoTopUpRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Settings saved.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AutoTopUpResponse"
                }
              }
            }
          },
          "400": {
            "description": "Bad request - invalid body, `enabled: true` without a card on file, `amountCents` outside 500..100000, `thresholdCents` negative, or `thresholdCents` not below `amountCents`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AutoTopUpErrorResponse"
                },
                "example": {
                  "error": "Add a payment method before turning on auto top-up"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized - invalid or missing authentication",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AutoTopUpErrorResponse"
                },
                "example": {
                  "error": "Unauthorized"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden - the account is not the caller's and not an organization they belong to (an unknown id also returns 403, never 404)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AutoTopUpErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "The account has no credits row and one could not be created for it",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AutoTopUpErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/credits/sessions": {
      "post": {
        "description": "Top up credits on the authenticated account.\n\n**Credits are integer micro-dollars (1,000,000 = \\$1.00, the same 6-decimal unit as USDC).** The customer is charged the USD value of `credits` plus a Stripe processing fee (US card pricing: 2.9% + \\$0.30) — e.g. `credits: 100000000` charges \\$103.30 total (\\$100.00 credits + \\$3.30 fee).\n\n**Two outcomes, distinguished by response shape:**\n\n- **Auto-charged** — if the account has a card on file (from a prior subscription or top-up), the card is charged immediately and the response is `{ paymentIntentId, creditsPurchased, totalCents }`. Credits land in the account's balance asynchronously via Stripe webhook (typically within seconds). No human interaction required.\n\n- **Checkout required** — if no card is on file, or the saved card requires 3-D Secure authentication, the response is `{ id, url }` with a hosted Stripe Checkout URL. Redirect to that URL; credits land on successful payment.\n\nClients should discriminate on the presence of `url` (Checkout) vs `paymentIntentId` (auto-charged). Cards entered through the Checkout fallback are saved for future top-ups, so a customer's second top-up typically auto-charges.",
        "security": [
          {
            "apiKeyAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "description": "Top-up parameters",
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateCreditsSessionRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Top-up initiated successfully. Some fields are conditional on which path was taken — see each field's description.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CreditsTopupResponse"
                }
              }
            }
          },
          "400": {
            "description": "Bad request - missing or invalid parameters",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CreditsSessionErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized - invalid or missing authentication",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CreditsSessionErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/subscriptions/claim": {
      "post": {
        "description": "Attach a subscription bought through [Create Subscription Session](/api-reference/subscriptions/sessions-create) (anonymous path) to the authenticated account. Needed only when the signed-in email differs from the email typed into Stripe Checkout; when they match, the webhook already linked the subscription and this call is a no-op that returns 200.",
        "security": [
          {
            "apiKeyAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "description": "The Checkout session to claim",
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ClaimSubscriptionRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Subscription attached to the authenticated account (or already was)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ClaimSubscriptionResponse"
                }
              }
            }
          },
          "400": {
            "description": "Bad request - missing `session_id`, or the session has no subscription",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ClaimSubscriptionErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized - invalid or missing authentication",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ClaimSubscriptionErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Unknown Checkout session",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ClaimSubscriptionErrorResponse"
                },
                "example": {
                  "status": "error",
                  "error": "session_not_found"
                }
              }
            }
          },
          "409": {
            "description": "The subscription belongs to another account that has signed in",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ClaimSubscriptionErrorResponse"
                },
                "example": {
                  "status": "error",
                  "error": "already_claimed"
                }
              }
            }
          },
          "500": {
            "description": "Internal server error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ClaimSubscriptionErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/pulses": {
      "get": {
        "description": "Retrieve pulse information for the authenticated account. If the account has access to organizations, pass account_id to filter to a specific account within those organizations.",
        "parameters": [
          {
            "name": "account_id",
            "in": "query",
            "description": "Filter to a specific account. Only applicable when the authenticated account has access to multiple accounts via organization membership.",
            "required": false,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "active",
            "in": "query",
            "description": "Filter by active status. Set to 'true' to return only active pulses, 'false' for inactive pulses. If not provided, returns all pulses regardless of active status.",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "true",
                "false"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Pulses retrieved successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PulsesResponse"
                }
              }
            }
          },
          "400": {
            "description": "Bad request - invalid query parameters",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized - invalid or missing API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden - account_id is not a member of the organization or account tried to filter by an account_id they don't have access to",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "patch": {
        "description": "Update the pulse settings for an account. Use this to enable or disable the pulse. Returns an array of pulses for consistency with the GET endpoint.",
        "requestBody": {
          "description": "Pulse fields to update",
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UpdatePulseRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Pulse updated successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PulsesResponse"
                }
              }
            }
          },
          "400": {
            "description": "Bad request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized - invalid or missing API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/emails": {
      "post": {
        "description": "Send an email to one or more recipients. Emails are sent from `Agent by Recoup <agent@recoupable.dev>`.",
        "requestBody": {
          "description": "Email payload",
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SendEmailRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Email sent successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/EmailResponse"
                }
              }
            }
          },
          "400": {
            "description": "Validation error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Authentication required",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "502": {
            "description": "Email delivery failed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/admins": {
      "get": {
        "description": "Check if the authenticated account is a Recoup admin. An account is considered an admin if it is a member of the Recoup organization. No input parameters required — authentication is performed via the x-api-key or Authorization header.",
        "parameters": [],
        "security": [
          {
            "apiKeyAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Admin status retrieved successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CheckAdminResponse"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized - missing or invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AccountErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/admins/sandboxes": {
      "get": {
        "description": "Returns a list of all accounts and their sandbox usage statistics. Each item includes the account email, total number of sandboxes created, and the timestamp of the most recently created sandbox. Requires the authenticated account to be a Recoup admin. Authentication via x-api-key or Authorization Bearer token.",
        "parameters": [],
        "security": [
          {
            "apiKeyAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Account sandbox statistics retrieved successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AdminSandboxesResponse"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized - missing or invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AccountErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden - authenticated account is not a Recoup admin",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AccountErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Internal server error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AccountErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/admins/sandboxes/orgs": {
      "get": {
        "description": "Returns commit statistics for each repository in the recoupable GitHub organization. Each item includes the repo name, URL, total commit count, latest 5 commit messages, earliest and latest commit timestamps, and the list of account repo URLs that include this org repo as a submodule. Requires the authenticated account to be a Recoup admin. Authentication via x-api-key or Authorization Bearer token.",
        "parameters": [],
        "security": [
          {
            "apiKeyAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Org repo statistics retrieved successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AdminSandboxOrgsResponse"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized - missing or invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AccountErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden - authenticated account is not a Recoup admin",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AccountErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Internal server error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AccountErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/admins/emails": {
      "get": {
        "description": "Returns Resend emails including HTML content. Provide either account_id (returns all emails for an account) or email_id (returns a single email by Resend ID). Requires the authenticated account to be a Recoup admin. Authentication via x-api-key or Authorization Bearer token. Email response shape matches the [Resend Retrieve Email API](https://resend.com/docs/api-reference/emails/retrieve-email).",
        "parameters": [
          {
            "name": "account_id",
            "in": "query",
            "description": "The account ID to fetch all emails for. Required if email_id is not provided.",
            "required": false,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "email_id",
            "in": "query",
            "description": "A Resend email ID to fetch a single email. Required if account_id is not provided.",
            "required": false,
            "schema": {
              "type": "string"
            }
          }
        ],
        "security": [
          {
            "apiKeyAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Emails retrieved successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AdminEmailsResponse"
                }
              }
            }
          },
          "400": {
            "description": "Bad request - must provide either account_id or email_id",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized - missing or invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AccountErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden - authenticated account is not a Recoup admin",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AccountErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Internal server error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AccountErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/admins/coding/slack": {
      "get": {
        "description": "Returns a list of Slack mentions of the Recoup Coding Agent bot, pulled directly from the Slack API as the source of truth. Each entry includes the tagger's information, the prompt they sent, the timestamp, the channel, and any GitHub pull request URLs opened in response. Also returns aggregate pull request statistics. Supports optional time-period filtering. Requires the authenticated account to be a Recoup admin. Authentication via x-api-key or Authorization Bearer token.",
        "parameters": [
          {
            "name": "period",
            "in": "query",
            "description": "Time period to filter tags. One of: all (no date filter), daily (last 24 hours), weekly (last 7 days), monthly (last 30 days). Defaults to all.",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "all",
                "daily",
                "weekly",
                "monthly"
              ],
              "default": "all"
            }
          }
        ],
        "security": [
          {
            "apiKeyAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Slack tag analytics retrieved successfully",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "status",
                    "total",
                    "total_pull_requests",
                    "tags_with_pull_requests",
                    "tags"
                  ],
                  "properties": {
                    "status": {
                      "type": "string",
                      "enum": [
                        "success"
                      ],
                      "description": "Status of the request"
                    },
                    "total": {
                      "type": "integer",
                      "description": "Total number of times the Coding Agent was tagged in the requested period",
                      "example": 134
                    },
                    "total_pull_requests": {
                      "type": "integer",
                      "description": "Total number of pull requests opened by the Coding Agent across all tags in the requested period",
                      "example": 121
                    },
                    "tags_with_pull_requests": {
                      "type": "integer",
                      "description": "Number of tags that resulted in at least one pull request being opened",
                      "example": 84
                    },
                    "tags": {
                      "type": "array",
                      "description": "List of Slack tag events",
                      "items": {
                        "type": "object",
                        "required": [
                          "user_id",
                          "user_name",
                          "prompt",
                          "timestamp",
                          "channel_id",
                          "channel_name"
                        ],
                        "properties": {
                          "user_id": {
                            "type": "string",
                            "description": "Slack ID of the person who tagged the agent",
                            "example": "U012AB3CD"
                          },
                          "user_name": {
                            "type": "string",
                            "description": "Display name of the person who tagged the agent",
                            "example": "Jane Smith"
                          },
                          "user_avatar": {
                            "type": [
                              "string",
                              "null"
                            ],
                            "description": "URL of the Slack avatar",
                            "example": "https://avatars.slack-edge.com/..."
                          },
                          "prompt": {
                            "type": "string",
                            "description": "The text of the message sent to the agent",
                            "example": "add dark mode support to the settings page"
                          },
                          "timestamp": {
                            "type": "string",
                            "format": "date-time",
                            "description": "ISO 8601 timestamp of the tag event",
                            "example": "2024-01-15T10:30:00.000Z"
                          },
                          "channel_id": {
                            "type": "string",
                            "description": "Slack channel ID where the tag occurred",
                            "example": "C012AB3CD"
                          },
                          "channel_name": {
                            "type": "string",
                            "description": "Human-readable name of the Slack channel",
                            "example": "dev-team"
                          },
                          "pull_requests": {
                            "type": "array",
                            "description": "GitHub pull request URLs opened by the Coding Agent in response to this prompt, parsed from bot replies in the Slack thread",
                            "items": {
                              "type": "string",
                              "format": "uri",
                              "example": "https://github.com/recoupable/api/pull/42"
                            },
                            "example": [
                              "https://github.com/recoupable/api/pull/42"
                            ]
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized - missing or invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AccountErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden - authenticated account is not a Recoup admin",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AccountErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Internal server error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AccountErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/admins/content/slack": {
      "get": {
        "description": "Returns a list of Slack mentions of the Recoup Content Agent bot, pulled directly from the Slack API as the source of truth. Each entry includes the tagger's information, the prompt they sent, the timestamp, the channel, and any video link responses. Also returns aggregate video statistics for measuring tag-to-video conversion. Supports optional time-period filtering. Requires the authenticated account to be a Recoup admin. Authentication via x-api-key or Authorization Bearer token.",
        "parameters": [
          {
            "name": "period",
            "in": "query",
            "description": "Time period to filter tags. One of: all (no date filter), daily (last 24 hours), weekly (last 7 days), monthly (last 30 days). Defaults to all.",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "all",
                "daily",
                "weekly",
                "monthly"
              ],
              "default": "all"
            }
          }
        ],
        "security": [
          {
            "apiKeyAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Slack tag analytics retrieved successfully",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "status",
                    "total",
                    "total_videos",
                    "tags_with_videos",
                    "tags"
                  ],
                  "properties": {
                    "status": {
                      "type": "string",
                      "enum": [
                        "success"
                      ],
                      "description": "Status of the request"
                    },
                    "total": {
                      "type": "integer",
                      "description": "Total number of times the Content Agent was tagged in the requested period",
                      "example": 18
                    },
                    "total_videos": {
                      "type": "integer",
                      "description": "Total number of videos generated by the Content Agent across all tags in the requested period",
                      "example": 12
                    },
                    "tags_with_videos": {
                      "type": "integer",
                      "description": "Number of tags that resulted in at least one video being generated",
                      "example": 10
                    },
                    "tags": {
                      "type": "array",
                      "description": "List of Slack tag events",
                      "items": {
                        "type": "object",
                        "required": [
                          "user_id",
                          "user_name",
                          "prompt",
                          "timestamp",
                          "channel_id",
                          "channel_name"
                        ],
                        "properties": {
                          "user_id": {
                            "type": "string",
                            "description": "Slack ID of the person who tagged the agent",
                            "example": "U012AB3CD"
                          },
                          "user_name": {
                            "type": "string",
                            "description": "Display name of the person who tagged the agent",
                            "example": "Jane Smith"
                          },
                          "user_avatar": {
                            "type": [
                              "string",
                              "null"
                            ],
                            "description": "URL of the Slack avatar",
                            "example": "https://avatars.slack-edge.com/..."
                          },
                          "prompt": {
                            "type": "string",
                            "description": "The text of the message sent to the agent",
                            "example": "create a highlight reel for the new single release"
                          },
                          "timestamp": {
                            "type": "string",
                            "format": "date-time",
                            "description": "ISO 8601 timestamp of the tag event",
                            "example": "2024-01-15T10:30:00.000Z"
                          },
                          "channel_id": {
                            "type": "string",
                            "description": "Slack channel ID where the tag occurred",
                            "example": "C012AB3CD"
                          },
                          "channel_name": {
                            "type": "string",
                            "description": "Human-readable name of the Slack channel",
                            "example": "content-team"
                          },
                          "video_links": {
                            "type": "array",
                            "description": "Video URLs generated by the Content Agent in response to this prompt, parsed from bot replies in the Slack thread",
                            "items": {
                              "type": "string",
                              "format": "uri",
                              "example": "https://recoupable.com/v/abc123"
                            },
                            "example": [
                              "https://recoupable.com/v/abc123"
                            ]
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized - missing or invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AccountErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden - authenticated account is not a Recoup admin",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AccountErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Internal server error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AccountErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/admins/coding/pr": {
      "get": {
        "description": "Returns the status (open, closed, or merged) for each provided GitHub pull request URL. Accepts one or more `pull_requests` query parameters containing GitHub PR URLs. Uses the GitHub REST API to check each pull request's state. Requires the authenticated account to be a Recoup admin. Authentication via x-api-key or Authorization Bearer token.",
        "parameters": [
          {
            "name": "pull_requests",
            "in": "query",
            "description": "One or more GitHub pull request URLs to check. Repeat this parameter for each URL. Example: ?pull_requests=https://github.com/org/repo/pull/1&pull_requests=https://github.com/org/repo/pull/2",
            "required": true,
            "schema": {
              "type": "array",
              "items": {
                "type": "string",
                "format": "uri"
              },
              "minItems": 1,
              "maxItems": 50
            },
            "style": "form",
            "explode": true
          }
        ],
        "security": [
          {
            "apiKeyAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "PR merged status retrieved successfully",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "status",
                    "pull_requests"
                  ],
                  "properties": {
                    "status": {
                      "type": "string",
                      "enum": [
                        "success"
                      ],
                      "description": "Status of the request"
                    },
                    "pull_requests": {
                      "type": "array",
                      "description": "Status for each provided pull request URL",
                      "items": {
                        "type": "object",
                        "required": [
                          "url",
                          "status"
                        ],
                        "properties": {
                          "url": {
                            "type": "string",
                            "format": "uri",
                            "description": "The GitHub pull request URL",
                            "example": "https://github.com/recoupable/api/pull/42"
                          },
                          "status": {
                            "type": "string",
                            "enum": [
                              "open",
                              "closed",
                              "merged"
                            ],
                            "description": "The current status of the pull request",
                            "example": "merged"
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Bad request - missing or invalid pull_requests parameter",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AccountErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized - missing or invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AccountErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden - authenticated account is not a Recoup admin",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AccountErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Internal server error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AccountErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/admins/privy": {
      "get": {
        "description": "Returns Privy login statistics for a given time period. Results include counts for new accounts (created_at), active accounts (latest_verified_at), total Privy accounts across all time, and the full, unmodified Privy account objects. See [Privy User object documentation](https://docs.privy.io/api-reference/users/get-all) for the complete type definition. Defaults to period=all (no date filter). Requires the authenticated account to be a Recoup admin. Authentication via x-api-key or Authorization Bearer token.",
        "parameters": [
          {
            "name": "period",
            "in": "query",
            "description": "Time period to filter logins. One of: all (no date filter), daily (last 24 hours), weekly (last 7 days), monthly (last 30 days). Defaults to all.",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "all",
                "daily",
                "weekly",
                "monthly"
              ],
              "default": "all"
            }
          }
        ],
        "security": [
          {
            "apiKeyAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Privy login statistics retrieved successfully",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "status",
                    "total",
                    "total_new",
                    "total_active",
                    "logins"
                  ],
                  "properties": {
                    "status": {
                      "type": "string",
                      "enum": [
                        "success"
                      ],
                      "description": "Status of the request"
                    },
                    "total": {
                      "type": "integer",
                      "description": "Total number of accounts in Privy across all time (not filtered by period)",
                      "example": 42
                    },
                    "total_new": {
                      "type": "integer",
                      "description": "Number of accounts created (created_at) within the requested period",
                      "example": 15
                    },
                    "total_active": {
                      "type": "integer",
                      "description": "Total number of accounts matching either new or active criteria in the requested period",
                      "example": 30
                    },
                    "logins": {
                      "type": "array",
                      "description": "Full, unmodified Privy account objects. See [Privy User object docs](https://docs.privy.io/api-reference/users/get-all) for the complete type definition.",
                      "items": {
                        "type": "object"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized - missing or invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AccountErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden - authenticated account is not a Recoup admin",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AccountErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Internal server error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AccountErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/admins/agent/signups": {
      "get": {
        "description": "Returns API key sign-up records created by AI agents. Agent sign-ups are identified by the agent+ email prefix in the associated account email. Supports period filtering and returns individual API key records with their associated email and creation timestamp, suitable for building sign-up trend charts. Requires admin authentication.",
        "parameters": [
          {
            "name": "period",
            "in": "query",
            "description": "Time period to filter sign-ups. One of: all (no date filter), daily (last 24 hours), weekly (last 7 days), monthly (last 30 days). Defaults to all.",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "all",
                "daily",
                "weekly",
                "monthly"
              ],
              "default": "all"
            }
          }
        ],
        "security": [
          {
            "apiKeyAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Agent sign-up records retrieved successfully",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "status",
                    "total",
                    "signups"
                  ],
                  "properties": {
                    "status": {
                      "type": "string",
                      "enum": [
                        "success"
                      ],
                      "description": "Status of the request"
                    },
                    "total": {
                      "type": "integer",
                      "description": "Total number of agent API key sign-ups matching the period filter",
                      "example": 42
                    },
                    "signups": {
                      "type": "array",
                      "description": "Array of agent API key sign-up records, ordered by creation date descending",
                      "items": {
                        "type": "object",
                        "required": [
                          "id",
                          "name",
                          "email",
                          "created_at"
                        ],
                        "properties": {
                          "id": {
                            "type": "string",
                            "format": "uuid",
                            "description": "Unique identifier of the API key record"
                          },
                          "name": {
                            "type": "string",
                            "description": "Name given to the API key"
                          },
                          "email": {
                            "type": "string",
                            "format": "email",
                            "description": "The agent+ prefixed email used to create the API key"
                          },
                          "created_at": {
                            "type": "string",
                            "format": "date-time",
                            "description": "Timestamp when the API key was created"
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized - missing or invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AccountErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden - authenticated account is not an admin",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AccountErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/admins/artists/pro": {
      "get": {
        "description": "Retrieve the deduplicated list of artist IDs owned by pro accounts. An account is \"pro\" when its email belongs to an enterprise domain or it has an active Stripe subscription. Admin-scoped: requires an admin-flagged API key because the response identifies paying customer accounts.",
        "security": [
          {
            "apiKeyAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Pro artist IDs retrieved successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ArtistsProResponse"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized - missing or invalid authentication"
          },
          "403": {
            "description": "Forbidden - caller is authenticated but not admin-scoped"
          },
          "500": {
            "description": "Internal server error while fetching pro artists",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ArtistsProResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/admins/credits": {
      "post": {
        "description": "Sets an account's credit balance, and records who set it and why. Requires the authenticated account to be a Recoup admin.\n\n**Absolute, not a delta.** `remaining_credits` is the balance the account is left holding, whatever it held before — the outcome never depends on the balance at the moment the request lands. The prior balance comes back as `previous_credits` for the record. There is deliberately no add/subtract variant.\n\n**Every grant is audited.** `reason` is required and must be non-empty. The grant is stored against the acting admin's account ID and is readable afterwards in the `grants` array of [`GET /api/admins/credits/events`](/api-reference/admins/credits-events).\n\n**A grant is never reduced by the monthly refill.** Credit balances are topped up to the plan total by a monthly refill that runs lazily on the next read of [`GET /api/accounts/{id}/credits`](/api-reference/accounts/credits-get), once the underlying row is more than a month old. That refill is a floor, not an assignment: it raises a balance up to the plan total and never lowers one. A grant above the plan total therefore survives every refill and holds until it is spent, so headroom granted once stays granted and does not need re-granting on a schedule.",
        "security": [
          {
            "apiKeyAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "description": "The account to set, the balance to leave it at, and why",
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AdminGrantCreditsRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Balance set and the grant recorded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AdminGrantCreditsResponse"
                }
              }
            }
          },
          "400": {
            "description": "Malformed JSON body, or a missing/invalid `account_id`, `remaining_credits`, or `reason`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AdminGrantCreditsValidationError"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized - missing or invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden - authenticated account is not a Recoup admin",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AccountErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "No account exists with the supplied `account_id`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AccountErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Internal server error while setting the balance or recording the grant",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AccountErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/admins/credits/rollup": {
      "get": {
        "description": "Returns the per-account rollup of credit usage over the selected period, sorted by total credits deducted descending. Each row carries the account identity (UUID + display name + primary email) and the aggregated spend so the admin dashboard can render a top-spenders table without joining client-side. Pair with [`GET /api/admins/credits/events?account_id=…&period=…`](/api-reference/admins/credits-events) to drill into the individual `usage_events` rows for a single account. Requires the authenticated account to be a Recoup admin.",
        "parameters": [
          {
            "name": "period",
            "in": "query",
            "required": false,
            "description": "Rollup window. `daily` = today (last 1 day), `weekly` = last 7 days, `monthly` = last 30 days, `all` = no cutoff (every event ever recorded). Defaults to `monthly`. Follows the existing admin period convention.",
            "schema": {
              "type": "string",
              "enum": [
                "all",
                "daily",
                "weekly",
                "monthly"
              ],
              "default": "monthly"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Page size — number of accounts to return per request, sorted by total credits deducted descending. Defaults to 100; max 500.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 500,
              "default": 100
            }
          },
          {
            "name": "page",
            "in": "query",
            "required": false,
            "description": "1-indexed page number. Server returns rows `(page - 1) * limit` through `page * limit - 1`. Defaults to 1. Use `total_count` in the response to drive a 'load more' control (`page * limit < total_count`).",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "default": 1
            }
          }
        ],
        "security": [
          {
            "apiKeyAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Credit usage rollup retrieved successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AdminCreditsRollupResponse"
                }
              }
            }
          },
          "400": {
            "description": "Invalid `period` or `limit` parameter",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AccountErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized - missing or invalid credentials"
          },
          "403": {
            "description": "Forbidden - authenticated account is not a Recoup admin"
          },
          "500": {
            "description": "Internal server error while computing the rollup",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AccountErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/admins/credits/events": {
      "get": {
        "description": "Returns the raw `usage_events` rows for a single account over the selected period, sorted by `created_at` descending. Powers the drilldown view that expands when a row in the [`/api/admins/credits/rollup`](/api-reference/admins/credits-rollup) table is opened. Each event represents one debit (main agent turn, subagent step, chat completion, or research call), with token counts and `credits_deducted` matching the wallet drop on `credits_usage` for that account.\n\nThe response also carries a `grants` array covering the same account and period: the admin credit grants made through [`POST /api/admins/credits`](/api-reference/admins/credits-grant). The two arrays answer different questions — `events` is what the system spent, `grants` is what a person set by hand, and only a grant carries an actor and a reason. Requires the authenticated account to be a Recoup admin.",
        "parameters": [
          {
            "name": "account_id",
            "in": "query",
            "required": true,
            "description": "UUID of the account whose `usage_events` rows to return.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "period",
            "in": "query",
            "required": false,
            "description": "Window to filter `created_at` against. Same semantics as the rollup endpoint. Defaults to `monthly`.",
            "schema": {
              "type": "string",
              "enum": [
                "all",
                "daily",
                "weekly",
                "monthly"
              ],
              "default": "monthly"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Page size — number of events to return per request, sorted by `created_at` descending. Defaults to 100; max 500.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 500,
              "default": 100
            }
          },
          {
            "name": "page",
            "in": "query",
            "required": false,
            "description": "1-indexed page number. Server returns rows `(page - 1) * limit` through `page * limit - 1`. Defaults to 1. Use `total_count` in the response to drive a 'load more' control on the drilldown (`page * limit < total_count`).",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "default": 1
            }
          }
        ],
        "security": [
          {
            "apiKeyAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Usage events retrieved successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AdminCreditsEventsResponse"
                }
              }
            }
          },
          "400": {
            "description": "Missing or invalid `account_id`, `period`, or `limit`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AccountErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized - missing or invalid credentials"
          },
          "403": {
            "description": "Forbidden - authenticated account is not a Recoup admin"
          },
          "500": {
            "description": "Internal server error while fetching usage events",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AccountErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/agents/signup": {
      "post": {
        "description": "Register an agent. For new agent+ emails, returns an API key immediately. For all other cases, sends a verification code to the email — call [POST /api/agents/verify](/api-reference/agents/verify) with the code to get your API key.\n\n**Tip:** If you're unsure what email to register, generate a unique `agent+{suffix}@recoupable.com` address (e.g. `agent+1744410896-28439@recoupable.com`, combining a Unix timestamp with a random integer). This guarantees a fresh `agent+` address on every call — including multiple signups within the same second — and returns an API key instantly with no email verification required.",
        "security": [],
        "requestBody": {
          "description": "Agent email to register",
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AgentSignupRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Signup processed successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AgentSignupResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/agents/verify": {
      "post": {
        "description": "Verify an agent's email with the code sent during signup. Returns an API key on success.",
        "security": [],
        "requestBody": {
          "description": "Email and verification code",
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AgentVerifyRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Verification successful",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AgentVerifyResponse"
                }
              }
            }
          },
          "400": {
            "description": "Invalid or expired verification code",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Too many failed verification attempts",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer"
      },
      "apiKeyAuth": {
        "type": "apiKey",
        "in": "header",
        "name": "x-api-key",
        "description": "Your Recoup API key. [Learn more](/quickstart#api-keys)."
      }
    },
    "schemas": {
      "AdminGrantCreditsRequest": {
        "type": "object",
        "required": [
          "account_id",
          "remaining_credits",
          "reason"
        ],
        "properties": {
          "account_id": {
            "type": "string",
            "format": "uuid",
            "description": "UUID of the account whose balance is being set. Must be an existing account — an unknown UUID is a 404, not a silent no-op."
          },
          "remaining_credits": {
            "type": "integer",
            "minimum": 0,
            "description": "The balance to leave the account holding, in credits (1 credit = 1 US cent). Absolute, not a delta. Zero is allowed — it is how an account is deliberately zeroed out. Negative values are rejected with a 400, even though ordinary usage can overdraw a balance below zero on its own."
          },
          "reason": {
            "type": "string",
            "minLength": 1,
            "description": "Why the grant was made, in plain language — e.g. `Trial headroom for the Aug 12 label demo`. Required and non-empty (a whitespace-only string is rejected): this is the field that makes a grant distinguishable from a Stripe top-up or a monthly reset when someone asks months later. Stored verbatim and returned by the events endpoint."
          }
        }
      },
      "AdminGrantCreditsResponse": {
        "type": "object",
        "required": [
          "status",
          "grant_id",
          "account_id",
          "remaining_credits",
          "previous_credits",
          "reason",
          "granted_by",
          "granted_at"
        ],
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "success"
            ],
            "description": "Status of the request"
          },
          "grant_id": {
            "type": "string",
            "format": "uuid",
            "description": "UUID of the recorded grant row. Matches the `id` of the corresponding entry in the `grants` array of `GET /api/admins/credits/events`."
          },
          "account_id": {
            "type": "string",
            "format": "uuid",
            "description": "UUID of the account whose balance was set (echoes the request)"
          },
          "remaining_credits": {
            "type": "integer",
            "description": "The balance the account now holds — the value supplied in the request"
          },
          "previous_credits": {
            "type": "integer",
            "nullable": true,
            "description": "The balance immediately before the grant. Null when the account had no credits row at all and one was created by this request."
          },
          "reason": {
            "type": "string",
            "description": "The reason recorded with the grant (echoes the request)"
          },
          "granted_by": {
            "type": "string",
            "format": "uuid",
            "description": "UUID of the admin account that made the grant, resolved from the credentials on the request. Never taken from the body."
          },
          "granted_at": {
            "type": "string",
            "format": "date-time",
            "description": "When the grant was recorded"
          }
        }
      },
      "AdminGrantCreditsValidationError": {
        "type": "object",
        "required": [
          "status",
          "error"
        ],
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "error"
            ],
            "description": "Status of the request"
          },
          "missing_fields": {
            "type": "array",
            "description": "JSON path segments of the first field that failed validation, e.g. `[\"reason\"]`. Absent when the body was not valid JSON at all.",
            "items": {
              "oneOf": [
                {
                  "type": "string"
                },
                {
                  "type": "integer"
                }
              ]
            }
          },
          "error": {
            "type": "string",
            "description": "Error message describing what went wrong"
          }
        }
      },
      "AdminCreditsRollupResponse": {
        "type": "object",
        "required": [
          "status",
          "period",
          "page",
          "limit",
          "total_count",
          "rows"
        ],
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "success",
              "error"
            ],
            "description": "Status of the request"
          },
          "period": {
            "type": "string",
            "enum": [
              "all",
              "daily",
              "weekly",
              "monthly"
            ],
            "description": "The period the response was computed over (echoes the request)"
          },
          "page": {
            "type": "integer",
            "minimum": 1,
            "description": "1-indexed page returned (echoes the request, or 1 if omitted)"
          },
          "limit": {
            "type": "integer",
            "minimum": 1,
            "description": "Page size used for this response (echoes the request, or the default if omitted)"
          },
          "total_count": {
            "type": "integer",
            "minimum": 0,
            "description": "Total number of distinct accounts with at least one `usage_events` row in the selected period (i.e. the size of the full result set before pagination). The client derives `has_more` as `page * limit < total_count` and shows 'X of Y accounts'. Computed via a `COUNT(DISTINCT account_id)` query alongside the paginated row fetch."
          },
          "rows": {
            "type": "array",
            "description": "Per-account credit usage totals, sorted by `total_credits_deducted` descending",
            "items": {
              "type": "object",
              "required": [
                "account_id",
                "total_credits_deducted",
                "event_count"
              ],
              "properties": {
                "account_id": {
                  "type": "string",
                  "format": "uuid",
                  "description": "UUID of the account"
                },
                "account_name": {
                  "type": "string",
                  "nullable": true,
                  "description": "Display name from the `accounts` table; null if the account has no name set"
                },
                "account_email": {
                  "type": "string",
                  "format": "email",
                  "nullable": true,
                  "description": "Primary email from `account_emails` joined for human-readable rows; null if no email is on file"
                },
                "total_credits_deducted": {
                  "type": "integer",
                  "description": "Sum of `usage_events.credits_deducted` across every event for this account in the selected period, in micro-dollars (1,000,000 = $1.00)"
                },
                "event_count": {
                  "type": "integer",
                  "description": "Number of `usage_events` rows aggregated into this total"
                }
              }
            }
          },
          "error": {
            "type": "string",
            "description": "Error message (only present if status is 'error')"
          }
        }
      },
      "AdminCreditsEventsResponse": {
        "type": "object",
        "required": [
          "status",
          "account_id",
          "period",
          "page",
          "limit",
          "total_count",
          "events",
          "grants"
        ],
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "success",
              "error"
            ],
            "description": "Status of the request"
          },
          "account_id": {
            "type": "string",
            "format": "uuid",
            "description": "UUID of the account whose events were returned (echoes the request)"
          },
          "period": {
            "type": "string",
            "enum": [
              "all",
              "daily",
              "weekly",
              "monthly"
            ],
            "description": "The period the response was filtered to (echoes the request)"
          },
          "page": {
            "type": "integer",
            "minimum": 1,
            "description": "1-indexed page returned (echoes the request, or 1 if omitted)"
          },
          "limit": {
            "type": "integer",
            "minimum": 1,
            "description": "Page size used for this response (echoes the request, or the default if omitted)"
          },
          "total_count": {
            "type": "integer",
            "minimum": 0,
            "description": "Total number of `usage_events` rows for this account in the selected period (i.e. the size of the full result set before pagination). The client derives `has_more` as `page * limit < total_count` and shows 'X of Y events'. Computed via a `COUNT(*)` query alongside the paginated row fetch."
          },
          "events": {
            "type": "array",
            "description": "Raw `usage_events` rows, sorted by `created_at` descending",
            "items": {
              "type": "object",
              "required": [
                "id",
                "created_at",
                "source",
                "agent_type",
                "input_tokens",
                "cached_input_tokens",
                "output_tokens",
                "tool_call_count",
                "credits_deducted"
              ],
              "properties": {
                "id": {
                  "type": "string",
                  "description": "nanoid primary key of the `usage_events` row"
                },
                "created_at": {
                  "type": "string",
                  "format": "date-time",
                  "description": "When the event was recorded"
                },
                "source": {
                  "type": "string",
                  "enum": [
                    "web",
                    "api"
                  ],
                  "description": "Which surface originated the debit. `web` = open-agents chat. `api` = recoupable api (chat completion or research call)."
                },
                "agent_type": {
                  "type": "string",
                  "enum": [
                    "main",
                    "subagent"
                  ],
                  "description": "`main` for the top-level agent turn or any non-agent debit; `subagent` for a nested task-tool step"
                },
                "provider": {
                  "type": "string",
                  "nullable": true,
                  "description": "Provider slug (e.g. `anthropic`). Null when the debit isn't tied to a specific provider (e.g. research)."
                },
                "model_id": {
                  "type": "string",
                  "nullable": true,
                  "description": "The model for LLM-backed debits (e.g. `anthropic/claude-opus-4.6`), otherwise the billed API endpoint as `METHOD /route/pattern` (e.g. `POST /api/research/web`). Null on rows written before 2026-08-27."
                },
                "input_tokens": {
                  "type": "integer",
                  "description": "Total input tokens for the call (including cached). 0 for non-LLM debits."
                },
                "cached_input_tokens": {
                  "type": "integer",
                  "description": "Subset of `input_tokens` that hit the provider cache (priced at the cache-read rate)."
                },
                "output_tokens": {
                  "type": "integer",
                  "description": "Output tokens for the call. 0 for non-LLM debits."
                },
                "tool_call_count": {
                  "type": "integer",
                  "description": "Number of tool calls observed in this step"
                },
                "credits_deducted": {
                  "type": "integer",
                  "description": "The amount debited from `credits_usage.remaining_credits` for this event, in micro-dollars (1,000,000 = $1.00). Same value the wallet dropped by on this turn."
                },
                "resource_url": {
                  "type": "string",
                  "nullable": true,
                  "description": "App-relative path of what produced the debit (`/chat?roomId=<roomId>`, `/music/<generationId>`, `/tasks/<taskId>/runs/<runId>`). Null for plain API calls and for rows written before the field existed."
                }
              }
            }
          },
          "grants": {
            "type": "array",
            "description": "Admin credit grants made against this account in the selected period, sorted by `created_at` descending — the write-side counterpart to `events`. A grant is a staff member setting the balance through [`POST /api/admins/credits`](/api-reference/admins/credits-grant), so unlike an event it carries an actor and a reason. Not paginated by `page`/`limit` and not counted in `total_count`, which describe `usage_events` only; capped at the 500 most recent grants in the period. Empty for the overwhelming majority of accounts, which have never been granted anything.",
            "items": {
              "type": "object",
              "required": [
                "id",
                "account_id",
                "created_at",
                "granted_by",
                "reason",
                "previous_credits",
                "remaining_credits"
              ],
              "properties": {
                "id": {
                  "type": "string",
                  "format": "uuid",
                  "description": "UUID of the grant row — the `grant_id` returned by `POST /api/admins/credits`"
                },
                "account_id": {
                  "type": "string",
                  "format": "uuid",
                  "description": "UUID of the account the grant was made against. Always equal to the top-level `account_id`, since the endpoint reports on one account at a time; present because each entry is the full stored grant row."
                },
                "created_at": {
                  "type": "string",
                  "format": "date-time",
                  "description": "When the grant was made"
                },
                "granted_by": {
                  "type": "string",
                  "format": "uuid",
                  "description": "UUID of the admin account that made the grant"
                },
                "reason": {
                  "type": "string",
                  "description": "The reason the admin recorded for the grant, verbatim"
                },
                "previous_credits": {
                  "type": "integer",
                  "nullable": true,
                  "description": "Balance immediately before the grant. Null when the account had no credits row and the grant created one."
                },
                "remaining_credits": {
                  "type": "integer",
                  "description": "Balance the account was left holding by the grant"
                }
              }
            }
          },
          "error": {
            "type": "string",
            "description": "Error message (only present if status is 'error')"
          }
        }
      },
      "ArtistsProResponse": {
        "type": "object",
        "required": [
          "status",
          "artists"
        ],
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "success",
              "error"
            ],
            "description": "Status of the request"
          },
          "artists": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "uuid"
            },
            "description": "List of artist IDs owned by pro accounts"
          },
          "error": {
            "type": "string",
            "description": "Error message (only present if status is 'error')"
          }
        }
      },
      "Account": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "description": "Unique identifier for the account"
          },
          "name": {
            "type": "string",
            "description": "Account display name"
          },
          "image": {
            "type": "string",
            "nullable": true,
            "description": "Profile image URL"
          },
          "instruction": {
            "type": "string",
            "nullable": true,
            "description": "Custom AI instructions for this account"
          },
          "knowledges": {
            "type": "array",
            "nullable": true,
            "items": {
              "$ref": "#/components/schemas/Knowledge"
            },
            "description": "Knowledge base files attached to this account"
          },
          "email": {
            "type": "string",
            "nullable": true,
            "description": "Primary email address"
          },
          "wallet_address": {
            "type": "string",
            "nullable": true,
            "description": "Connected wallet address"
          }
        }
      },
      "AccountData": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "description": "The unique identifier of the account"
          },
          "account_id": {
            "type": "string",
            "format": "uuid",
            "description": "The account ID (same as id, for consistency)"
          },
          "name": {
            "type": "string",
            "description": "Display name of the account"
          },
          "email": {
            "type": "string",
            "format": "email",
            "description": "Email address associated with the account"
          },
          "wallet": {
            "type": "string",
            "description": "Wallet address associated with the account"
          },
          "image": {
            "type": "string",
            "format": "uri",
            "description": "URL of the account's profile image"
          },
          "instruction": {
            "type": "string",
            "description": "Custom instruction or bio"
          },
          "organization": {
            "type": "string",
            "description": "Organization name"
          },
          "job_title": {
            "type": "string",
            "description": "Job title"
          },
          "role_type": {
            "type": "string",
            "description": "Role type"
          },
          "company_name": {
            "type": "string",
            "description": "Company name"
          },
          "knowledges": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Knowledge"
            },
            "description": "Knowledge base files attached to this account"
          }
        }
      },
      "AccountDataResponse": {
        "type": "object",
        "required": [
          "data"
        ],
        "properties": {
          "data": {
            "$ref": "#/components/schemas/AccountData",
            "description": "The account data"
          }
        }
      },
      "AccountErrorResponse": {
        "type": "object",
        "required": [
          "status",
          "message"
        ],
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "error"
            ],
            "description": "Status of the request"
          },
          "message": {
            "type": "string",
            "description": "Error message describing what went wrong"
          }
        }
      },
      "AccountSandboxRow": {
        "type": "object",
        "required": [
          "account_id",
          "total_sandboxes",
          "last_created_at"
        ],
        "properties": {
          "account_id": {
            "type": "string",
            "format": "uuid",
            "description": "The unique identifier of the account",
            "example": "04e3aba9-c130-4fb8-8b92-34e95d43e66b"
          },
          "total_sandboxes": {
            "type": "integer",
            "description": "Total number of sandboxes created for this account",
            "example": 5
          },
          "last_created_at": {
            "type": "string",
            "format": "date-time",
            "description": "ISO 8601 timestamp of the most recently created sandbox for this account",
            "example": "2026-03-10T12:00:00Z"
          },
          "account_email": {
            "type": "string",
            "nullable": true,
            "description": "The email address of the account. Null if the account has no email set.",
            "example": "alice@example.com"
          }
        },
        "description": ""
      },
      "AddArtistSuccessResponse": {
        "type": "object",
        "required": [
          "success"
        ],
        "properties": {
          "success": {
            "type": "boolean",
            "enum": [
              true
            ],
            "description": "Indicates the artist was successfully added"
          }
        }
      },
      "AddArtistToAccountRequest": {
        "type": "object",
        "required": [
          "email",
          "artistId"
        ],
        "properties": {
          "email": {
            "type": "string",
            "format": "email",
            "description": "Email address of the account to add the artist to"
          },
          "artistId": {
            "type": "string",
            "format": "uuid",
            "description": "The unique identifier of the artist to add"
          }
        }
      },
      "AddArtistToOrganizationRequest": {
        "type": "object",
        "required": [
          "artistId",
          "organizationId"
        ],
        "properties": {
          "artistId": {
            "type": "string",
            "format": "uuid",
            "description": "The account ID of the artist to add",
            "example": "artist-account-uuid"
          },
          "organizationId": {
            "type": "string",
            "format": "uuid",
            "description": "The account ID of the organization",
            "example": "org-account-uuid"
          }
        }
      },
      "AddArtistToOrganizationResponse": {
        "type": "object",
        "required": [
          "status",
          "id"
        ],
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "success"
            ],
            "description": "Status of the request"
          },
          "id": {
            "type": "string",
            "format": "uuid",
            "description": "UUID of the created artist-organization link"
          }
        }
      },
      "AddOrganizationMemberRequest": {
        "type": "object",
        "required": [
          "organizationId"
        ],
        "properties": {
          "organizationId": {
            "type": "string",
            "format": "uuid",
            "description": "The account ID of the organization"
          },
          "accountId": {
            "type": "string",
            "format": "uuid",
            "description": "The account ID of the member to add. Provide exactly one of accountId or email."
          },
          "email": {
            "type": "string",
            "format": "email",
            "description": "Email of the member to add. The account is created if it does not exist yet. Provide exactly one of accountId or email."
          }
        }
      },
      "AddOrganizationMemberResponse": {
        "type": "object",
        "required": [
          "status",
          "id",
          "account_id"
        ],
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "success"
            ],
            "description": "Status of the request"
          },
          "id": {
            "type": "string",
            "format": "uuid",
            "description": "UUID of the membership record"
          },
          "account_id": {
            "type": "string",
            "format": "uuid",
            "description": "The account ID of the member (resolved from email when email was provided)"
          }
        }
      },
      "RemoveOrganizationMemberResponse": {
        "type": "object",
        "required": [
          "status"
        ],
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "success"
            ],
            "description": "Status of the request"
          }
        }
      },
      "OrganizationDomain": {
        "type": "object",
        "required": [
          "id",
          "domain",
          "organization_id"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "description": "UUID of the domain mapping record"
          },
          "domain": {
            "type": "string",
            "description": "The email domain (lowercase, no @)",
            "example": "seekermusic.com"
          },
          "organization_id": {
            "type": "string",
            "format": "uuid",
            "description": "The account ID of the organization"
          },
          "created_at": {
            "type": "string",
            "format": "date-time",
            "description": "When the mapping was created"
          }
        }
      },
      "GetOrganizationDomainsResponse": {
        "type": "object",
        "required": [
          "status",
          "domains"
        ],
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "success"
            ],
            "description": "Status of the request"
          },
          "domains": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/OrganizationDomain"
            },
            "description": "Domains mapped to the organization"
          }
        }
      },
      "AddOrganizationDomainRequest": {
        "type": "object",
        "required": [
          "organizationId",
          "domain"
        ],
        "properties": {
          "organizationId": {
            "type": "string",
            "format": "uuid",
            "description": "The account ID of the organization"
          },
          "domain": {
            "type": "string",
            "description": "The email domain to map (lowercase, no @)",
            "example": "seekermusic.com"
          }
        }
      },
      "AddOrganizationDomainResponse": {
        "type": "object",
        "required": [
          "status",
          "id",
          "domain",
          "organization_id"
        ],
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "success"
            ],
            "description": "Status of the request"
          },
          "id": {
            "type": "string",
            "format": "uuid",
            "description": "UUID of the domain mapping record"
          },
          "domain": {
            "type": "string",
            "description": "The mapped email domain",
            "example": "seekermusic.com"
          },
          "organization_id": {
            "type": "string",
            "format": "uuid",
            "description": "The account ID of the organization"
          }
        }
      },
      "RemoveOrganizationDomainResponse": {
        "type": "object",
        "required": [
          "status"
        ],
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "success"
            ],
            "description": "Status of the request"
          }
        }
      },
      "AdminEmailsResponse": {
        "type": "object",
        "required": [
          "status",
          "emails"
        ],
        "description": "Response containing Resend emails sent for an account",
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "success",
              "error"
            ],
            "description": "Status of the request"
          },
          "emails": {
            "type": "array",
            "description": "List of emails sent for the account, ordered by created_at descending",
            "items": {
              "$ref": "#/components/schemas/PulseEmailRow"
            }
          }
        }
      },
      "AdminSandboxOrgsResponse": {
        "type": "object",
        "required": [
          "status",
          "repos"
        ],
        "description": "Response containing commit statistics for all org repositories",
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "success",
              "error"
            ],
            "description": "Status of the request"
          },
          "repos": {
            "type": "array",
            "description": "List of org repos with their commit statistics, ordered by total_commits descending",
            "items": {
              "$ref": "#/components/schemas/OrgRepoRow"
            }
          }
        }
      },
      "AdminSandboxesResponse": {
        "type": "object",
        "required": [
          "status",
          "accounts"
        ],
        "description": "Response containing per-account sandbox statistics for admin use",
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "success",
              "error"
            ],
            "description": "Status of the request"
          },
          "accounts": {
            "type": "array",
            "description": "List of accounts with their sandbox statistics, ordered by most recently active first",
            "items": {
              "$ref": "#/components/schemas/AccountSandboxRow"
            }
          }
        }
      },
      "CheckAdminResponse": {
        "type": "object",
        "required": [
          "status",
          "isAdmin"
        ],
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "success"
            ],
            "description": "Status of the request"
          },
          "isAdmin": {
            "type": "boolean",
            "description": "Whether the authenticated account is a Recoup admin"
          }
        }
      },
      "CreateAccountRequest": {
        "type": "object",
        "properties": {
          "email": {
            "type": "string",
            "format": "email",
            "description": "Email address to associate with the account. If an account with this email exists, it will be returned."
          },
          "wallet": {
            "type": "string",
            "description": "Wallet address to associate with the account. If an account with this wallet exists, it will be returned."
          }
        },
        "description": "At least one of email or wallet should be provided to identify or create an account."
      },
      "SendEmailRequest": {
        "type": "object",
        "properties": {
          "to": {
            "type": "array",
            "minItems": 1,
            "items": {
              "type": "string",
              "format": "email"
            },
            "description": "Recipient email address(es). Optional — when omitted, the email is sent to the authenticated account's own email address. Without a payment method on file, restricted to the account's own email; [add a payment method](/api-reference/credits/sessions-create) to send to any recipient.",
            "example": [
              "recipient@example.com"
            ]
          },
          "cc": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "email"
            },
            "description": "Optional CC email addresses. Without a payment method on file, restricted to the account's own email; [add a payment method](/api-reference/credits/sessions-create) to send to any recipient.",
            "example": [
              "cc@example.com"
            ]
          },
          "subject": {
            "type": "string",
            "description": "Email subject line. Optional — when omitted, defaults to the first heading or line of the body, falling back to `Message from Recoup` if the body is empty.",
            "example": "Weekly Pulse Report"
          },
          "text": {
            "type": "string",
            "description": "Plain text or Markdown body. Rendered as HTML via Markdown if no `html` is provided.",
            "example": "# Pulse Report\n\nHere's your weekly summary."
          },
          "html": {
            "type": "string",
            "description": "Raw HTML body. Takes precedence over `text` when both are provided.",
            "example": "<h1>Pulse Report</h1><p>Here's your weekly summary.</p>"
          },
          "headers": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            },
            "description": "Optional custom email headers"
          },
          "chat_id": {
            "type": "string",
            "description": "Chat ID to include a chat link in the email footer. Use [Create Chat](/api-reference/chat/create) to create one."
          },
          "account_id": {
            "type": "string",
            "format": "uuid",
            "description": "UUID of the account to send the email for. Only applicable when the authenticated account has access to multiple accounts via organization membership. If not provided, sends for the API key's own account."
          }
        }
      },
      "EmailResponse": {
        "type": "object",
        "required": [
          "success",
          "message",
          "id"
        ],
        "properties": {
          "success": {
            "type": "boolean",
            "description": "Whether the email was sent successfully",
            "example": true
          },
          "message": {
            "type": "string",
            "description": "Human-readable result message",
            "example": "Email sent successfully from Agent by Recoup <agent@recoupable.dev> to recipient@example.com."
          },
          "id": {
            "type": "string",
            "description": "Resend email ID",
            "example": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
          }
        }
      },
      "CreateOrganizationRequest": {
        "type": "object",
        "required": [
          "name",
          "accountId"
        ],
        "properties": {
          "name": {
            "type": "string",
            "description": "The name of the organization to create",
            "example": "My New Label"
          },
          "accountId": {
            "type": "string",
            "format": "uuid",
            "description": "The account ID of the creator",
            "example": "123e4567-e89b-12d3-a456-426614174000"
          }
        }
      },
      "CreateOrganizationResponse": {
        "type": "object",
        "required": [
          "status",
          "organization"
        ],
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "success"
            ],
            "description": "Status of the request"
          },
          "organization": {
            "$ref": "#/components/schemas/CreatedOrganization",
            "description": "The created organization"
          }
        }
      },
      "CreatedOrganization": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "description": "UUID of the new organization account"
          },
          "name": {
            "type": "string",
            "description": "Name of the organization"
          }
        }
      },
      "Error": {
        "required": [
          "error",
          "message"
        ],
        "type": "object",
        "properties": {
          "error": {
            "type": "integer",
            "format": "int32"
          },
          "message": {
            "type": "string"
          }
        }
      },
      "GetAccountIdResponse": {
        "type": "object",
        "required": [
          "status",
          "accountId"
        ],
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "success"
            ],
            "description": "Status of the request"
          },
          "accountId": {
            "type": "string",
            "format": "uuid",
            "description": "The unique identifier (UUID) of the authenticated account"
          }
        }
      },
      "GetAccountResponse": {
        "type": "object",
        "required": [
          "status",
          "account"
        ],
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "success"
            ],
            "description": "Status of the request"
          },
          "account": {
            "$ref": "#/components/schemas/Account",
            "description": "The account details"
          }
        }
      },
      "GetOrganizationsResponse": {
        "type": "object",
        "required": [
          "status",
          "organizations"
        ],
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "success"
            ],
            "description": "Status of the request"
          },
          "organizations": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Organization"
            },
            "description": "List of organizations the account belongs to"
          }
        }
      },
      "Knowledge": {
        "type": "object",
        "properties": {
          "url": {
            "type": "string",
            "description": "URL to the knowledge file"
          },
          "name": {
            "type": "string",
            "description": "Name of the knowledge file"
          },
          "type": {
            "type": "string",
            "description": "MIME type of the file"
          }
        }
      },
      "OrgRepoRow": {
        "type": "object",
        "required": [
          "repo_name",
          "repo_url",
          "total_commits",
          "latest_commit_messages",
          "earliest_committed_at",
          "latest_committed_at",
          "account_repos"
        ],
        "description": "Commit statistics for a single GitHub org repository",
        "properties": {
          "repo_name": {
            "type": "string",
            "description": "Repository name",
            "example": "chat"
          },
          "repo_url": {
            "type": "string",
            "description": "Full GitHub HTML URL of the repository",
            "example": "https://github.com/recoupable/chat"
          },
          "total_commits": {
            "type": "integer",
            "description": "Total number of commits in the repository",
            "example": 5696
          },
          "latest_commit_messages": {
            "type": "array",
            "description": "Messages from the 5 most recent commits",
            "items": {
              "type": "string"
            },
            "example": [
              "Merge test into main",
              "fix: duration stuck at 0ms for in-progress tasks"
            ]
          },
          "earliest_committed_at": {
            "type": "string",
            "format": "date-time",
            "description": "ISO 8601 timestamp of the earliest (first) commit",
            "example": "2024-09-27T17:16:01Z"
          },
          "latest_committed_at": {
            "type": "string",
            "format": "date-time",
            "description": "ISO 8601 timestamp of the most recent commit",
            "example": "2026-03-10T19:57:37Z"
          },
          "account_repos": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "account_id": {
                  "type": "string"
                },
                "email": {
                  "type": "string",
                  "nullable": true
                },
                "repo_url": {
                  "type": "string"
                }
              },
              "required": [
                "account_id",
                "repo_url"
              ]
            },
            "description": "List of accounts using this org repo as a submodule, with account_id, email, and repo_url"
          }
        }
      },
      "Organization": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "description": "UUID of the membership record"
          },
          "organization_id": {
            "type": "string",
            "format": "uuid",
            "description": "UUID of the organization account"
          },
          "organization_name": {
            "type": "string",
            "description": "Display name of the organization"
          },
          "organization_image": {
            "type": "string",
            "nullable": true,
            "description": "Organization logo/image URL"
          }
        }
      },
      "OrganizationsErrorResponse": {
        "type": "object",
        "required": [
          "status",
          "message"
        ],
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "error"
            ],
            "description": "Status of the request"
          },
          "message": {
            "type": "string",
            "description": "Error message describing what went wrong"
          }
        }
      },
      "ErrorEnvelope": {
        "type": "object",
        "required": [
          "status",
          "error"
        ],
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "error"
            ],
            "description": "Status of the request"
          },
          "error": {
            "type": "string",
            "description": "Error message describing what went wrong"
          }
        }
      },
      "Pulse": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "description": "Unique identifier for the pulse"
          },
          "account_id": {
            "type": "string",
            "format": "uuid",
            "description": "Unique identifier for the associated account"
          },
          "active": {
            "type": "boolean",
            "description": "Whether the pulse is active (defaults to true)"
          }
        }
      },
      "PulseEmailRow": {
        "type": "object",
        "required": [
          "id",
          "from",
          "to",
          "subject",
          "created_at",
          "last_event"
        ],
        "description": "A single email record from Resend (full GetEmailResponseSuccess). See [Resend API docs](https://resend.com/docs/api-reference/emails/retrieve-email) for the source of truth.",
        "properties": {
          "id": {
            "type": "string",
            "description": "The Resend email ID"
          },
          "from": {
            "type": "string",
            "description": "Sender email address"
          },
          "to": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Recipient email addresses"
          },
          "cc": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "nullable": true,
            "description": "CC recipient email addresses"
          },
          "bcc": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "nullable": true,
            "description": "BCC recipient email addresses"
          },
          "reply_to": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "nullable": true,
            "description": "Reply-to email addresses"
          },
          "subject": {
            "type": "string",
            "description": "Email subject line"
          },
          "html": {
            "type": "string",
            "nullable": true,
            "description": "HTML content of the email"
          },
          "text": {
            "type": "string",
            "nullable": true,
            "description": "Plain text content of the email"
          },
          "created_at": {
            "type": "string",
            "format": "date-time",
            "description": "Timestamp when the email was created"
          },
          "scheduled_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true,
            "description": "Scheduled send time, if any"
          },
          "last_event": {
            "type": "string",
            "enum": [
              "bounced",
              "canceled",
              "clicked",
              "complained",
              "delivered",
              "delivery_delayed",
              "failed",
              "opened",
              "queued",
              "scheduled",
              "sent"
            ],
            "description": "Most recent delivery event for this email"
          },
          "tags": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "name": {
                  "type": "string"
                },
                "value": {
                  "type": "string"
                }
              },
              "required": [
                "name",
                "value"
              ]
            },
            "nullable": true,
            "description": "Custom tags attached to the email"
          }
        }
      },
      "PulsesResponse": {
        "type": "object",
        "required": [
          "status",
          "pulses"
        ],
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "success",
              "error"
            ],
            "description": "Status of the request"
          },
          "pulses": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Pulse"
            },
            "description": "Array of pulse objects. Contains pulses for the authenticated account. If the account has access to organizations, all organization accounts are included."
          },
          "error": {
            "type": "string",
            "description": "Error message (only present if status is error)"
          }
        }
      },
      "UpdateAccountRequest": {
        "type": "object",
        "required": [
          "accountId"
        ],
        "properties": {
          "accountId": {
            "type": "string",
            "format": "uuid",
            "description": "The unique identifier of the account to update"
          },
          "name": {
            "type": "string",
            "description": "Display name for the account"
          },
          "instruction": {
            "type": "string",
            "description": "Custom instruction or bio for the account"
          },
          "organization": {
            "type": "string",
            "description": "Organization name associated with the account"
          },
          "image": {
            "type": "string",
            "format": "uri",
            "description": "URL of the account's profile image"
          },
          "jobTitle": {
            "type": "string",
            "description": "Job title of the account holder"
          },
          "roleType": {
            "type": "string",
            "description": "Role type within the organization"
          },
          "companyName": {
            "type": "string",
            "description": "Company name associated with the account"
          },
          "knowledges": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Knowledge"
            },
            "description": "Knowledge base files attached to this account"
          }
        }
      },
      "UpdatePulseRequest": {
        "type": "object",
        "required": [
          "active"
        ],
        "properties": {
          "active": {
            "type": "boolean",
            "description": "Whether to enable or disable the pulse",
            "example": true
          },
          "account_id": {
            "type": "string",
            "format": "uuid",
            "description": "UUID of the account to update the pulse for. Only applicable when the authenticated account has access to multiple accounts via organization membership. If not provided, updates the pulse for the API key's own account."
          }
        }
      },
      "AgentSignupRequest": {
        "type": "object",
        "required": [
          "email"
        ],
        "properties": {
          "email": {
            "type": "string",
            "format": "email",
            "description": "The agent email address. Emails with the agent+ prefix (e.g. agent+mybot@example.com) get an API key immediately on first signup.",
            "example": "agent+mybot@example.com"
          }
        }
      },
      "AgentSignupResponse": {
        "type": "object",
        "required": [
          "account_id",
          "message"
        ],
        "properties": {
          "account_id": {
            "type": "string",
            "format": "uuid",
            "description": "The account ID for the registered agent.",
            "example": "123e4567-e89b-12d3-a456-426614174000"
          },
          "api_key": {
            "type": [
              "string",
              "null"
            ],
            "description": "API key returned immediately for new agent+ prefix emails. Null for all other cases — check your email for a verification code.",
            "example": "recoup_sk_abc123..."
          },
          "message": {
            "type": "string",
            "description": "Human-readable status message.",
            "example": "If this is a new agent+ email, your API key is included. Otherwise, check your email for a verification code."
          }
        }
      },
      "AgentVerifyRequest": {
        "type": "object",
        "required": [
          "email",
          "code"
        ],
        "properties": {
          "email": {
            "type": "string",
            "format": "email",
            "description": "The email address used during signup.",
            "example": "you@example.com"
          },
          "code": {
            "type": "string",
            "minLength": 6,
            "maxLength": 6,
            "pattern": "^[0-9]{6}$",
            "description": "The 6-digit verification code sent to the email.",
            "example": "123456"
          }
        }
      },
      "AgentVerifyResponse": {
        "type": "object",
        "required": [
          "account_id",
          "api_key",
          "message"
        ],
        "properties": {
          "account_id": {
            "type": "string",
            "format": "uuid",
            "description": "The account ID for the verified agent.",
            "example": "123e4567-e89b-12d3-a456-426614174000"
          },
          "api_key": {
            "type": "string",
            "description": "API key for the verified account.",
            "example": "recoup_sk_abc123..."
          },
          "message": {
            "type": "string",
            "description": "Human-readable status message.",
            "example": "Verified"
          }
        }
      },
      "CreateSubscriptionSessionRequest": {
        "type": "object",
        "required": [
          "successUrl"
        ],
        "properties": {
          "plan": {
            "type": "string",
            "enum": [
              "starter",
              "pro"
            ],
            "description": "The plan to buy. Defaults to `pro` when omitted (back-compat for authenticated callers). `pro` carries the 30-day trial; `starter` charges on completion.",
            "example": "pro"
          },
          "successUrl": {
            "type": "string",
            "format": "uri",
            "description": "Where Stripe sends the buyer after payment. For anonymous checkout include the literal `{CHECKOUT_SESSION_ID}` placeholder so the client can call Claim Subscription if the sign-in email differs.",
            "example": "https://chat.recoupable.dev?checkout=success&session_id={CHECKOUT_SESSION_ID}"
          },
          "cancelUrl": {
            "type": "string",
            "format": "uri",
            "description": "Where Stripe sends the buyer if they leave Checkout without paying.",
            "example": "https://recoupable.dev/pricing"
          }
        }
      },
      "CreateCardOnFileSessionRequest": {
        "type": "object",
        "required": [
          "successUrl"
        ],
        "properties": {
          "successUrl": {
            "type": "string",
            "format": "uri",
            "description": "The URL Stripe redirects to after the card is saved.",
            "example": "https://chat.recoupable.dev?card=saved"
          }
        }
      },
      "CreateSubscriptionSessionResponse": {
        "type": "object",
        "required": [
          "id",
          "url"
        ],
        "properties": {
          "id": {
            "type": "string",
            "description": "The checkout session ID.",
            "example": "cs_test_a1b2c3d4e5f6g7h8i9j0"
          },
          "url": {
            "type": "string",
            "format": "uri",
            "description": "The hosted checkout URL. Redirect to this URL to complete Checkout.",
            "example": "https://checkout.stripe.com/pay/cs_test_a1b2c3d4e5f6g7h8i9j0"
          }
        }
      },
      "SubscriptionResponse": {
        "type": "object",
        "required": [
          "isPro",
          "status",
          "plan",
          "source",
          "name",
          "amountCents",
          "currency",
          "interval",
          "collectionMethod",
          "currentPeriodEnd"
        ],
        "properties": {
          "isPro": {
            "type": "boolean",
            "description": "True when the account has an active paid subscription, directly or via an organization."
          },
          "status": {
            "type": "string",
            "enum": [
              "active",
              "trialing",
              "canceled",
              "past_due",
              "none"
            ],
            "description": "Lifecycle state of the subscription. `none` when no subscription exists.",
            "example": "active"
          },
          "plan": {
            "type": "string",
            "nullable": true,
            "description": "Plan identifier (e.g. `pro`). Null when no subscription exists.",
            "example": "pro"
          },
          "source": {
            "type": "string",
            "enum": [
              "account",
              "organization"
            ],
            "nullable": true,
            "description": "Origin of the subscription. `account` for direct, `organization` when covered by an organization. Null when no subscription exists.",
            "example": "account"
          },
          "name": {
            "type": "string",
            "nullable": true,
            "description": "Display name of the plan's Stripe price (nickname) or product. Null when no subscription exists.",
            "example": "Pro"
          },
          "amountCents": {
            "type": "integer",
            "nullable": true,
            "description": "Recurring amount in the smallest currency unit (cents for USD). Null when no subscription exists.",
            "example": 9900
          },
          "currency": {
            "type": "string",
            "nullable": true,
            "description": "ISO 4217 currency code, lowercase.",
            "example": "usd"
          },
          "interval": {
            "type": "string",
            "enum": [
              "day",
              "week",
              "month",
              "year"
            ],
            "nullable": true,
            "description": "Billing interval of the recurring price.",
            "example": "month"
          },
          "collectionMethod": {
            "type": "string",
            "enum": [
              "charge_automatically",
              "send_invoice"
            ],
            "nullable": true,
            "description": "`charge_automatically` bills the card on file at renewal; `send_invoice` emails an invoice that is paid manually (enterprise plans). A saved card is never charged for a `send_invoice` plan.",
            "example": "charge_automatically"
          },
          "currentPeriodEnd": {
            "type": "string",
            "format": "date-time",
            "nullable": true,
            "description": "End of the current billing period: the renewal date, or the next invoice date for invoiced plans.",
            "example": "2026-10-04T00:00:00Z"
          }
        },
        "description": "Subscription that confers the account's plan, directly or via an organization. `plan`, `source`, and the six billing fields (`name`, `amountCents`, `currency`, `interval`, `collectionMethod`, `currentPeriodEnd`) are all null when `status` is `none`."
      },
      "SubscriptionErrorResponse": {
        "type": "object",
        "required": [
          "error"
        ],
        "properties": {
          "error": {
            "type": "string",
            "description": "Human-readable error message.",
            "example": "Account not found"
          }
        }
      },
      "AccountCreditsResponse": {
        "type": "object",
        "required": [
          "account_id",
          "remaining_credits",
          "total_credits",
          "used_credits",
          "is_pro",
          "timestamp"
        ],
        "properties": {
          "account_id": {
            "type": "string",
            "format": "uuid",
            "description": "The unique identifier of the account this balance belongs to.",
            "example": "550e8400-e29b-41d4-a716-446655440000"
          },
          "remaining_credits": {
            "type": "integer",
            "minimum": 0,
            "description": "Credits still available in the current monthly period. Refills automatically once the period elapses.",
            "example": 87
          },
          "total_credits": {
            "type": "integer",
            "minimum": 0,
            "description": "Total credits granted per monthly period for the account's current plan. Free accounts and pro accounts receive different allotments.",
            "example": 100
          },
          "used_credits": {
            "type": "integer",
            "minimum": 0,
            "description": "Credits consumed in the current monthly period. Equal to `total_credits - remaining_credits`.",
            "example": 13
          },
          "is_pro": {
            "type": "boolean",
            "description": "True when the account is on a pro plan, directly via an account subscription or via an organization subscription.",
            "example": false
          },
          "timestamp": {
            "type": "string",
            "format": "date-time",
            "nullable": true,
            "description": "ISO 8601 timestamp of the last balance update or monthly refill. Null when the account has never spent or refilled credits.",
            "example": "2026-05-01T12:34:56.000Z"
          }
        }
      },
      "AccountUsageEvent": {
        "type": "object",
        "required": [
          "id",
          "created_at",
          "source",
          "agent_type",
          "provider",
          "model_id",
          "input_tokens",
          "cached_input_tokens",
          "output_tokens",
          "tool_call_count",
          "credits_deducted",
          "usd",
          "resource_url"
        ],
        "properties": {
          "id": {
            "type": "string",
            "description": "Identifier of the `usage_events` row.",
            "example": "3AANn3Ij9uF-zZIlW_zlP"
          },
          "created_at": {
            "type": "string",
            "format": "date-time",
            "description": "When the charge was recorded.",
            "example": "2026-08-27T11:56:58.000Z"
          },
          "source": {
            "type": "string",
            "description": "Which surface originated the charge: `api` for a request to the API, `web` for the chat app.",
            "example": "api"
          },
          "agent_type": {
            "type": "string",
            "description": "`main` for a top-level request or any non-agent charge; `subagent` for a nested task step.",
            "example": "main"
          },
          "provider": {
            "type": "string",
            "nullable": true,
            "description": "Provider slug when the charge is a model or generation call (for example `anthropic`, `fal`). Null for research and other fixed-price endpoints.",
            "example": "fal"
          },
          "model_id": {
            "type": "string",
            "nullable": true,
            "description": "The model for LLM-backed charges (a chat turn, a song generation), otherwise the API endpoint that was billed, as `METHOD /route/pattern` (for example `POST /api/artist/socials/scrape`). Null on rows written before 2026-08-27.",
            "example": "POST /api/artist/socials/scrape"
          },
          "input_tokens": {
            "type": "integer",
            "description": "Input tokens for the call, including cached. 0 for non-LLM charges.",
            "example": 0
          },
          "cached_input_tokens": {
            "type": "integer",
            "description": "Subset of `input_tokens` served from the provider cache.",
            "example": 0
          },
          "output_tokens": {
            "type": "integer",
            "description": "Output tokens for the call. 0 for non-LLM charges.",
            "example": 0
          },
          "tool_call_count": {
            "type": "integer",
            "description": "Number of tool calls in this step.",
            "example": 0
          },
          "credits_deducted": {
            "type": "integer",
            "description": "Amount charged, as the raw ledger integer in micro-dollars (1,000,000 = $1.00). Divide by 1,000,000 for dollars.",
            "example": 20000
          },
          "usd": {
            "type": "string",
            "description": "`credits_deducted` formatted as US dollars with two decimals.",
            "example": "$0.02"
          },
          "resource_url": {
            "type": "string",
            "nullable": true,
            "description": "App-relative path of what produced the charge, when there is something to open: `/chat?roomId=<roomId>` for a chat turn, `/music/<generationId>` for a song, `/tasks/<taskId>/runs/<runId>` for a scheduled task run. Null for plain API calls and for rows written before the field existed.",
            "example": "/music/0c35429f-deb8-48f0-b0f2-fd5145de2583"
          }
        }
      },
      "AccountUsageResponse": {
        "type": "object",
        "required": [
          "account_id",
          "period",
          "total_credits_deducted",
          "total_usd",
          "events",
          "next_cursor"
        ],
        "properties": {
          "account_id": {
            "type": "string",
            "format": "uuid",
            "description": "The account whose charges are listed.",
            "example": "550e8400-e29b-41d4-a716-446655440000"
          },
          "period": {
            "type": "object",
            "required": [
              "from",
              "to"
            ],
            "description": "The period the totals cover.",
            "properties": {
              "from": {
                "type": "string",
                "format": "date-time",
                "example": "2026-08-01T00:00:00.000Z"
              },
              "to": {
                "type": "string",
                "format": "date-time",
                "example": "2026-08-27T12:00:00.000Z"
              }
            }
          },
          "total_credits_deducted": {
            "type": "integer",
            "description": "Sum of `credits_deducted` over every charge in the period, in micro-dollars. Covers the whole period, not only this page.",
            "example": 70000
          },
          "total_usd": {
            "type": "string",
            "description": "`total_credits_deducted` formatted as US dollars.",
            "example": "$0.07"
          },
          "events": {
            "type": "array",
            "description": "Charges in the period in the requested `sort` order (newest first by default).",
            "items": {
              "$ref": "#/components/schemas/AccountUsageEvent"
            }
          },
          "next_cursor": {
            "type": "string",
            "nullable": true,
            "description": "Opaque; pass as `cursor` (with the same `sort`, `from` and `to`) to fetch the next page. Null when the page was the last one in the period.",
            "example": "2026-08-27T11:56:58.000Z"
          },
          "series_bucket": {
            "type": "string",
            "enum": [
              "hour",
              "day",
              "week",
              "month"
            ],
            "description": "Granularity of `series`, derived from the span of the period: `hour` up to 2 days, `day` up to 90 days, `week` up to 12 months, `month` beyond. Present only on a first page (no `cursor`).",
            "example": "day"
          },
          "series": {
            "type": "array",
            "description": "Spend over the period, one entry per `series_bucket` that had at least one charge, ascending by `start`, in UTC. The sum of `credits_deducted` across the entries equals `total_credits_deducted`. Present only on a first page (no `cursor`); omitted on cursor pages so paging never recomputes it.",
            "items": {
              "type": "object",
              "required": [
                "start",
                "credits_deducted",
                "usd",
                "events"
              ],
              "properties": {
                "start": {
                  "type": "string",
                  "format": "date-time",
                  "description": "Start of the bucket, UTC.",
                  "example": "2026-08-12T00:00:00.000Z"
                },
                "credits_deducted": {
                  "type": "integer",
                  "description": "Charges in the bucket, in micro-dollars.",
                  "example": 221090000
                },
                "usd": {
                  "type": "string",
                  "description": "`credits_deducted` formatted as US dollars.",
                  "example": "$221.09"
                },
                "events": {
                  "type": "integer",
                  "description": "Number of charges in the bucket.",
                  "example": 2462
                }
              }
            },
            "example": [
              {
                "start": "2026-08-11T00:00:00.000Z",
                "credits_deducted": 56080000,
                "usd": "$56.08",
                "events": 727
              },
              {
                "start": "2026-08-12T00:00:00.000Z",
                "credits_deducted": 221090000,
                "usd": "$221.09",
                "events": 2462
              },
              {
                "start": "2026-08-13T00:00:00.000Z",
                "credits_deducted": 440930000,
                "usd": "$440.93",
                "events": 5479
              }
            ]
          }
        }
      },
      "AccountCreditsErrorResponse": {
        "type": "object",
        "required": [
          "error"
        ],
        "properties": {
          "error": {
            "type": "string",
            "description": "Human-readable error message.",
            "example": "Account not found"
          }
        }
      },
      "SavedCard": {
        "type": "object",
        "required": [
          "brand",
          "last4",
          "exp_month",
          "exp_year",
          "funding"
        ],
        "properties": {
          "brand": {
            "type": "string",
            "description": "Card brand. Common values: `visa`, `mastercard`, `amex`, `discover`, `diners`, `jcb`, `unionpay`, `unknown`.",
            "example": "visa"
          },
          "last4": {
            "type": "string",
            "pattern": "^\\d{4}$",
            "description": "Last four digits of the card number. Safe to display in UI for identification.",
            "example": "4242"
          },
          "exp_month": {
            "type": "integer",
            "minimum": 1,
            "maximum": 12,
            "description": "Card expiration month (1-12). Compare against the current month to detect expired cards.",
            "example": 12
          },
          "exp_year": {
            "type": "integer",
            "description": "Card expiration year (full 4-digit year). Compare against the current year to detect expired cards.",
            "example": 2026
          },
          "funding": {
            "type": "string",
            "description": "Funding type. Common values: `credit`, `debit`, `prepaid`, `unknown`.",
            "example": "credit"
          }
        }
      },
      "AccountPaymentMethodResponse": {
        "type": "object",
        "required": [
          "account_id",
          "card"
        ],
        "properties": {
          "account_id": {
            "type": "string",
            "format": "uuid",
            "description": "The unique identifier of the account this payment method belongs to.",
            "example": "550e8400-e29b-41d4-a716-446655440000"
          },
          "card": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/SavedCard"
              },
              {
                "type": "null"
              }
            ],
            "description": "Default card on file. `null` when the account has never saved a payment method — the top-up flow will route through a checkout session in that case."
          }
        }
      },
      "AccountPaymentMethodErrorResponse": {
        "type": "object",
        "required": [
          "error"
        ],
        "properties": {
          "error": {
            "type": "string",
            "description": "Human-readable error message.",
            "example": "Unauthorized"
          }
        }
      },
      "AccountPayment": {
        "type": "object",
        "required": [
          "id",
          "createdAt",
          "description",
          "amountCents",
          "currency",
          "status",
          "url"
        ],
        "properties": {
          "id": {
            "type": "string",
            "description": "Stripe invoice id; use as `startingAfter` to page.",
            "example": "in_1U5xj400JObOnOb5BE0CmxCt"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time",
            "description": "When the invoice was created.",
            "example": "2026-09-04T14:33:00Z"
          },
          "description": {
            "type": "string",
            "description": "What was billed: the first line item's description, or the plan name.",
            "example": "Pro, monthly"
          },
          "amountCents": {
            "type": "integer",
            "description": "Amount due in the smallest currency unit.",
            "example": 9900
          },
          "currency": {
            "type": "string",
            "description": "ISO 4217 currency code, lowercase.",
            "example": "usd"
          },
          "status": {
            "type": "string",
            "enum": [
              "draft",
              "open",
              "paid",
              "uncollectible",
              "void"
            ],
            "description": "Stripe invoice status.",
            "example": "paid"
          },
          "url": {
            "type": "string",
            "format": "uri",
            "nullable": true,
            "description": "Hosted invoice page (receipt). Null for draft invoices.",
            "example": "https://invoice.stripe.com/i/acct_123/test_456"
          }
        }
      },
      "AccountPaymentsResponse": {
        "type": "object",
        "required": [
          "account_id",
          "payments",
          "hasMore"
        ],
        "properties": {
          "account_id": {
            "type": "string",
            "format": "uuid",
            "description": "The account these payments belong to.",
            "example": "550e8400-e29b-41d4-a716-446655440000"
          },
          "payments": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/AccountPayment"
            },
            "description": "Invoices, newest first. Empty when the account has no Stripe customer or no invoices."
          },
          "hasMore": {
            "type": "boolean",
            "description": "True when another page exists; pass the last `id` as `startingAfter`.",
            "example": false
          }
        }
      },
      "AccountPaymentsErrorResponse": {
        "type": "object",
        "required": [
          "error"
        ],
        "properties": {
          "error": {
            "type": "string",
            "description": "Human-readable error message.",
            "example": "Account not found"
          }
        }
      },
      "AutoTopUpResponse": {
        "type": "object",
        "required": [
          "account_id",
          "enabled",
          "amountCents",
          "thresholdCents",
          "lastRunAt",
          "lastError"
        ],
        "properties": {
          "account_id": {
            "type": "string",
            "format": "uuid",
            "example": "550e8400-e29b-41d4-a716-446655440000"
          },
          "enabled": {
            "type": "boolean",
            "description": "Whether auto top-up is on. Off by default.",
            "example": true
          },
          "amountCents": {
            "type": "integer",
            "nullable": true,
            "description": "Amount charged and granted per top-up, in cents. Null until set.",
            "example": 10000
          },
          "thresholdCents": {
            "type": "integer",
            "nullable": true,
            "description": "Balance, in cents, below which a top-up runs. Null until set.",
            "example": 100
          },
          "lastRunAt": {
            "type": "string",
            "format": "date-time",
            "nullable": true,
            "description": "When the last auto top-up was attempted. Null until the first run.",
            "example": "2026-09-04T15:05:00Z"
          },
          "lastError": {
            "type": "string",
            "nullable": true,
            "description": "Stripe decline message from the attempt that turned auto top-up off. Null while healthy or once re-enabled.",
            "example": "Your card was declined."
          }
        }
      },
      "UpdateAutoTopUpRequest": {
        "type": "object",
        "required": [
          "enabled",
          "amountCents",
          "thresholdCents"
        ],
        "properties": {
          "enabled": {
            "type": "boolean",
            "example": true
          },
          "amountCents": {
            "type": "integer",
            "minimum": 500,
            "maximum": 100000,
            "description": "Amount to charge and grant per top-up, in cents (5.00 to 1,000.00 USD).",
            "example": 10000
          },
          "thresholdCents": {
            "type": "integer",
            "minimum": 0,
            "description": "Balance, in cents, below which a top-up runs. Must be below `amountCents`.",
            "example": 100
          }
        }
      },
      "AutoTopUpErrorResponse": {
        "type": "object",
        "required": [
          "error"
        ],
        "properties": {
          "error": {
            "type": "string",
            "description": "Human-readable error message.",
            "example": "Add a payment method before turning on auto top-up"
          }
        }
      },
      "CreateCreditsSessionRequest": {
        "type": "object",
        "required": [
          "credits"
        ],
        "properties": {
          "successUrl": {
            "type": "string",
            "format": "uri",
            "description": "The URL to redirect the customer to after a successful Stripe Checkout payment. Only used when the response is a Checkout fallback (no card on file or 3-D Secure required) — ignored when the card on file is charged directly. Defaults to a generic Recoup success page if omitted.",
            "example": "https://chat.recoupable.com?credits=success"
          },
          "credits": {
            "type": "integer",
            "minimum": 1,
            "description": "The number of credits to purchase, in integer micro-dollars (1,000,000 = \\$1.00), so the value in USD is `credits / 1000000`. Example: `2500000` is \\$2.50 worth of credits (the customer is also charged a Stripe processing fee on top).",
            "example": 250
          },
          "accountId": {
            "type": "string",
            "format": "uuid",
            "description": "UUID of the account to create the session for. Only applicable when the authenticated account has admin access to multiple accounts. If not provided, creates a session for the API key's own account.",
            "example": "123e4567-e89b-12d3-a456-426614174000"
          }
        }
      },
      "CreditsTopupResponse": {
        "type": "object",
        "description": "Top-up response. Two paths share this shape — clients can discriminate on the presence of `url` (Checkout fallback) vs `paymentIntentId` (auto-charged).",
        "properties": {
          "paymentIntentId": {
            "type": "string",
            "description": "**Auto-charge path only.** The Stripe PaymentIntent ID for the off-session charge. Useful for reconciliation and support. Omitted on the Checkout fallback path.",
            "example": "pi_3O1a2b3c4d5e6f7g_h8i9j0kL"
          },
          "creditsPurchased": {
            "type": "integer",
            "minimum": 1,
            "description": "**Auto-charge path only.** The number of credits purchased, matching the `credits` request field. These credits are added to the account's balance asynchronously via Stripe webhook (typically within seconds). Omitted on the Checkout fallback path.",
            "example": 250
          },
          "totalCents": {
            "type": "integer",
            "minimum": 1,
            "description": "**Auto-charge path only.** Total amount in cents charged to the saved card, equal to `creditsPurchased` plus the Stripe processing fee. Omitted on the Checkout fallback path.",
            "example": 289
          },
          "id": {
            "type": "string",
            "description": "**Checkout fallback only.** The Stripe Checkout Session ID. Omitted on the auto-charge path.",
            "example": "cs_test_a1b2c3d4e5f6g7h8i9j0"
          },
          "url": {
            "type": "string",
            "format": "uri",
            "description": "**Checkout fallback only.** The hosted Stripe Checkout URL — redirect the customer here to enter card details and complete payment. Omitted on the auto-charge path.",
            "example": "https://checkout.stripe.com/pay/cs_test_a1b2c3d4e5f6g7h8i9j0"
          }
        }
      },
      "CreditsSessionErrorResponse": {
        "type": "object",
        "required": [
          "error"
        ],
        "properties": {
          "error": {
            "type": "string",
            "description": "Human-readable error message.",
            "example": "credits must be a positive integer"
          }
        }
      },
      "SubscriptionSessionErrorResponse": {
        "type": "object",
        "required": [
          "error"
        ],
        "properties": {
          "error": {
            "type": "string",
            "description": "Human-readable error message.",
            "example": "successUrl is required"
          }
        }
      },
      "ClaimSubscriptionRequest": {
        "type": "object",
        "required": [
          "session_id"
        ],
        "properties": {
          "session_id": {
            "type": "string",
            "description": "The Stripe Checkout session id from the `session_id` query parameter on the success URL.",
            "example": "cs_live_a1b2c3d4e5f6g7h8i9j0"
          }
        }
      },
      "ClaimSubscriptionResponse": {
        "type": "object",
        "required": [
          "status",
          "subscription_id",
          "plan"
        ],
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "success"
            ]
          },
          "subscription_id": {
            "type": "string",
            "description": "The Stripe subscription now attached to the authenticated account.",
            "example": "sub_1Rz0abc123"
          },
          "plan": {
            "type": "string",
            "enum": [
              "starter",
              "pro"
            ],
            "description": "The plan the subscription is on, resolved from its Stripe price.",
            "example": "pro"
          }
        }
      },
      "ClaimSubscriptionErrorResponse": {
        "type": "object",
        "required": [
          "status",
          "error"
        ],
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "error"
            ]
          },
          "error": {
            "type": "string",
            "description": "Machine-readable code (`session_not_found`, `already_claimed`, `no_subscription`) or a validation message.",
            "example": "already_claimed"
          }
        }
      },
      "SubscriptionPortalErrorResponse": {
        "type": "object",
        "required": [
          "error"
        ],
        "properties": {
          "error": {
            "type": "string",
            "description": "Human-readable error message.",
            "example": "returnUrl is required"
          }
        }
      },
      "CreateSubscriptionPortalRequest": {
        "type": "object",
        "required": [
          "returnUrl"
        ],
        "description": "Request body for creating a billing portal session. The account is taken from the `id` path parameter and must be accessible to the authenticated caller.",
        "properties": {
          "returnUrl": {
            "type": "string",
            "format": "uri",
            "description": "The URL to redirect to when the customer leaves the portal.",
            "example": "https://chat.recoupable.com/settings/billing"
          }
        }
      },
      "CreateSubscriptionPortalResponse": {
        "type": "object",
        "required": [
          "id",
          "url"
        ],
        "properties": {
          "id": {
            "type": "string",
            "description": "The portal session ID.",
            "example": "portal_sess_a1b2c3d4"
          },
          "url": {
            "type": "string",
            "format": "uri",
            "description": "The hosted portal URL. Redirect to this URL so the customer can manage their subscription.",
            "example": "https://billing.example.com/manage/portal_sess_a1b2c3d4"
          }
        }
      }
    }
  }
}