openapi: 3.0.3 info: title: Raptor API description: | Self-hosted webhook/email/DNS capture & inspection. This document is the spec-first source of truth for the versioned management API under `/api/v1`. The SPA is a pure client of it. **Authentication.** When `--require-auth` is enabled, send `Api-Key: `. With no key configured the API is open (first-run bootstrap mode). version: "1.0" servers: - url: /api/v1 paths: /groups: get: summary: List groups operationId: listGroups responses: "200": description: All groups content: application/json: schema: type: object properties: data: type: array items: { $ref: "#/components/schemas/Group" } post: summary: Create a group operationId: createGroup requestBody: required: true content: application/json: schema: type: object required: [name] properties: name: { type: string } color: { type: string } responses: "201": description: Created group content: application/json: schema: { $ref: "#/components/schemas/Group" } /groups/{groupID}: parameters: - { name: groupID, in: path, required: true, schema: { type: string } } put: summary: Update a group operationId: updateGroup requestBody: required: true content: application/json: schema: type: object properties: name: { type: string } color: { type: string } responses: "200": description: Updated group content: application/json: schema: { $ref: "#/components/schemas/Group" } delete: summary: Delete a group (tokens are kept and detached) operationId: deleteGroup responses: "204": { description: Deleted } "404": { description: Not found } /schedules: get: summary: List schedules operationId: listSchedules responses: "200": description: Schedules content: application/json: schema: type: object properties: data: { type: array, items: { $ref: "#/components/schemas/Schedule" } } post: summary: Create a schedule operationId: createSchedule requestBody: required: true content: application/json: schema: { $ref: "#/components/schemas/ScheduleInput" } responses: "201": description: Created schedule content: application/json: schema: { $ref: "#/components/schemas/Schedule" } /schedules/{scheduleID}: parameters: - { name: scheduleID, in: path, required: true, schema: { type: string } } get: summary: Get a schedule operationId: getSchedule responses: "200": description: Schedule content: application/json: schema: { $ref: "#/components/schemas/Schedule" } put: summary: Update a schedule operationId: updateSchedule requestBody: required: true content: application/json: schema: { $ref: "#/components/schemas/ScheduleInput" } responses: "200": description: Updated schedule content: application/json: schema: { $ref: "#/components/schemas/Schedule" } delete: summary: Delete a schedule operationId: deleteSchedule responses: "204": { description: Deleted } /schedules/{scheduleID}/runs: parameters: - { name: scheduleID, in: path, required: true, schema: { type: string } } get: summary: List a schedule's run history operationId: listScheduleRuns responses: "200": description: Runs content: application/json: schema: type: object properties: data: { type: array, items: { $ref: "#/components/schemas/ScheduleRun" } } /schedules/{scheduleID}/run: parameters: - { name: scheduleID, in: path, required: true, schema: { type: string } } post: summary: Run a schedule immediately operationId: runScheduleNow responses: "200": description: Run result content: application/json: schema: { $ref: "#/components/schemas/ScheduleRun" } /tokens/{tokenID}/replay: parameters: - $ref: "#/components/parameters/TokenID" post: summary: Replay a subset of captured requests to a target URL operationId: replayRequests requestBody: required: true content: application/json: schema: type: object required: [target_url] properties: target_url: { type: string } q: { type: string, description: "search DSL to select the subset" } date_from: { type: string } date_to: { type: string } limit: { type: integer } responses: "200": description: Replay counts content: application/json: schema: type: object properties: replayed: { type: integer } failed: { type: integer } /auth/status: get: summary: Auth/bootstrap status and current user operationId: authStatus responses: "200": description: Status content: application/json: schema: type: object properties: bootstrapped: { type: boolean } require_auth: { type: boolean } allow_registration: { type: boolean } authenticated: { type: boolean } user: { $ref: "#/components/schemas/User" } /auth/register: post: summary: Register an account (first one becomes admin; adopts anonymous URLs) operationId: register requestBody: required: true content: application/json: schema: { $ref: "#/components/schemas/Credentials" } responses: "201": { description: Account created and signed in } "403": { description: Registration disabled } /auth/bootstrap: post: summary: Create the first admin (only when no users exist) operationId: bootstrap requestBody: required: true content: application/json: schema: { $ref: "#/components/schemas/Credentials" } responses: "201": { description: Admin created and signed in } "409": { description: Already initialised } /auth/login: post: summary: Sign in (sets a session cookie) operationId: login requestBody: required: true content: application/json: schema: { $ref: "#/components/schemas/Credentials" } responses: "200": { description: Signed in } "401": { description: Invalid credentials } /auth/logout: post: summary: Sign out operationId: logout responses: "204": { description: Signed out } /auth/me: get: summary: Current authenticated user operationId: me responses: "200": description: User content: application/json: schema: { $ref: "#/components/schemas/User" } "401": { description: Not authenticated } /account/api-keys: get: summary: List the current user's API keys operationId: listAPIKeys responses: "200": description: API keys content: application/json: schema: type: object properties: data: { type: array, items: { $ref: "#/components/schemas/APIKey" } } post: summary: Create an API key (plaintext returned once) operationId: createAPIKey requestBody: required: false content: application/json: schema: type: object properties: { name: { type: string } } responses: "201": description: Created key (the `key` field is shown only here) content: application/json: schema: type: object properties: key: { type: string } api_key: { $ref: "#/components/schemas/APIKey" } /account/api-keys/{keyID}: parameters: - { name: keyID, in: path, required: true, schema: { type: string } } delete: summary: Revoke an API key operationId: deleteAPIKey responses: "204": { description: Revoked } /users: get: summary: List users (admin) operationId: listUsers responses: "200": description: Users content: application/json: schema: type: object properties: data: { type: array, items: { $ref: "#/components/schemas/User" } } "403": { description: Admin role required } post: summary: Create a user (admin) operationId: createUser requestBody: required: true content: application/json: schema: type: object required: [email, password] properties: email: { type: string } password: { type: string } role: { type: string, enum: [admin, user] } responses: "201": description: Created user content: application/json: schema: { $ref: "#/components/schemas/User" } /users/{userID}: parameters: - { name: userID, in: path, required: true, schema: { type: string } } put: summary: Update a user (admin) operationId: updateUser requestBody: required: true content: application/json: schema: type: object properties: email: { type: string } password: { type: string } role: { type: string, enum: [admin, user] } responses: "200": description: Updated user content: application/json: schema: { $ref: "#/components/schemas/User" } delete: summary: Delete a user (admin) operationId: deleteUser responses: "204": { description: Deleted } /action-types: get: summary: List available Custom Action type names operationId: listActionTypes responses: "200": description: Action type names content: application/json: schema: type: object properties: data: { type: array, items: { type: string } } /tokens/{tokenID}/actions: parameters: - $ref: "#/components/parameters/TokenID" get: summary: List a token's Custom Actions (in execution order) operationId: listActions responses: "200": description: Actions content: application/json: schema: type: object properties: data: { type: array, items: { $ref: "#/components/schemas/Action" } } post: summary: Add a Custom Action operationId: createAction requestBody: required: true content: application/json: schema: { $ref: "#/components/schemas/ActionInput" } responses: "201": description: Created action content: application/json: schema: { $ref: "#/components/schemas/Action" } /tokens/{tokenID}/actions/{actionID}: parameters: - $ref: "#/components/parameters/TokenID" - { name: actionID, in: path, required: true, schema: { type: string } } put: summary: Update a Custom Action operationId: updateAction requestBody: required: true content: application/json: schema: { $ref: "#/components/schemas/ActionInput" } responses: "200": description: Updated action content: application/json: schema: { $ref: "#/components/schemas/Action" } delete: summary: Delete a Custom Action operationId: deleteAction responses: "204": { description: Deleted } /tokens/{tokenID}/test-action: parameters: - $ref: "#/components/parameters/TokenID" post: summary: Test a single action definition against the latest request operationId: testAction requestBody: required: true content: application/json: schema: { $ref: "#/components/schemas/ActionInput" } responses: "200": description: Test result (output, error, variables, response) content: application/json: schema: { type: object } /tokens/{tokenID}/requests/{requestID}/action-runs: parameters: - $ref: "#/components/parameters/TokenID" - $ref: "#/components/parameters/RequestID" get: summary: List the action run log for a request operationId: listActionRuns responses: "200": description: Action runs content: application/json: schema: type: object properties: data: { type: array, items: { $ref: "#/components/schemas/ActionRun" } } /tokens/{tokenID}/requests/{requestID}/response: parameters: - $ref: "#/components/parameters/TokenID" - $ref: "#/components/parameters/RequestID" post: summary: Supply the response for a pending request (CLI listen flow) operationId: setResponse description: > When the token has `listen > 0`, a capture is held open until this endpoint supplies a response. Body may be base64 (`content`) or raw (`content_text`). requestBody: required: true content: application/json: schema: type: object properties: status: { type: integer } content: { type: string, description: "base64-encoded body" } content_text: { type: string, description: "raw body (alternative to content)" } headers: { type: object, additionalProperties: { type: string } } responses: "204": { description: Response delivered } "404": { description: No request is waiting (not listening / already responded) } /tokens/{tokenID}/requests/{requestID}/execute: parameters: - $ref: "#/components/parameters/TokenID" - $ref: "#/components/parameters/RequestID" post: summary: Replay the action chain against a stored request operationId: executeChain responses: "200": description: Resulting action runs content: application/json: schema: type: object properties: data: { type: array, items: { $ref: "#/components/schemas/ActionRun" } } /tokens: get: summary: List tokens operationId: listTokens responses: "200": description: All tokens content: application/json: schema: type: object properties: data: type: array items: { $ref: "#/components/schemas/Token" } post: summary: Create a token operationId: createToken requestBody: required: false content: application/json: schema: { $ref: "#/components/schemas/TokenInput" } responses: "201": description: Created token content: application/json: schema: { $ref: "#/components/schemas/Token" } "409": { description: Alias already in use } /tokens/{tokenID}: parameters: - $ref: "#/components/parameters/TokenID" get: summary: Get a token operationId: getToken responses: "200": description: Token content: application/json: schema: { $ref: "#/components/schemas/Token" } "404": { description: Not found } put: summary: Update a token operationId: updateToken requestBody: required: true content: application/json: schema: { $ref: "#/components/schemas/TokenInput" } responses: "200": description: Updated token content: application/json: schema: { $ref: "#/components/schemas/Token" } delete: summary: Delete a token and all its data operationId: deleteToken responses: "204": { description: Deleted } "404": { description: Not found } /tokens/{tokenID}/stream: parameters: - $ref: "#/components/parameters/TokenID" get: summary: Stream newly captured requests (Server-Sent Events) operationId: streamRequests description: | Opens a long-lived `text/event-stream`. Each captured request is sent as an `event: request` with the JSON request object as `data`. Comment lines (`: ping`) are heartbeats. responses: "200": description: SSE stream content: text/event-stream: schema: { type: string } /tokens/{tokenID}/requests: parameters: - $ref: "#/components/parameters/TokenID" get: summary: List captured requests (paged, newest first) operationId: listRequests parameters: - { name: page, in: query, schema: { type: integer, default: 1 } } - { name: per_page, in: query, schema: { type: integer, default: 50, maximum: 100 } } - name: q in: query description: > Search DSL. Free text matches content; field filters include `content:`, `method:`, `type:`, `ip:`, `host:`, `url:`, `query:`, `headers.:`, `_exists_:`, and `created_at:[* TO now-14d]`. Terms are ANDed; quote values with spaces. schema: { type: string } - { name: date_from, in: query, description: "ISO date/datetime lower bound", schema: { type: string } } - { name: date_to, in: query, description: "ISO date/datetime upper bound", schema: { type: string } } responses: "200": description: Page of requests content: application/json: schema: type: object properties: data: type: array items: { $ref: "#/components/schemas/Request" } total: { type: integer } page: { type: integer } per_page: { type: integer } delete: summary: Delete requests for the token (all, or a `q`/`date_from`/`date_to` subset) operationId: deleteAllRequests parameters: - { name: q, in: query, description: "search DSL; deletes only matching requests", schema: { type: string } } - { name: date_from, in: query, schema: { type: string } } - { name: date_to, in: query, schema: { type: string } } responses: "200": description: Number deleted content: application/json: schema: type: object properties: deleted: { type: integer } /tokens/{tokenID}/requests.csv: parameters: - $ref: "#/components/parameters/TokenID" get: summary: Export all captured requests as CSV operationId: exportCSV responses: "200": description: CSV download content: text/csv: schema: { type: string } /tokens/{tokenID}/requests/latest: parameters: - $ref: "#/components/parameters/TokenID" get: summary: Get the most recent request operationId: latestRequest responses: "200": description: Request content: application/json: schema: { $ref: "#/components/schemas/Request" } "404": { description: No requests yet } /tokens/{tokenID}/requests/{requestID}: parameters: - $ref: "#/components/parameters/TokenID" - $ref: "#/components/parameters/RequestID" get: summary: Get a single request operationId: getRequest responses: "200": description: Request content: application/json: schema: { $ref: "#/components/schemas/Request" } "404": { description: Not found } delete: summary: Delete a single request operationId: deleteRequest responses: "204": { description: Deleted } "404": { description: Not found } /tokens/{tokenID}/requests/{requestID}/raw: parameters: - $ref: "#/components/parameters/TokenID" - $ref: "#/components/parameters/RequestID" get: summary: Get the raw HTTP representation of a request operationId: rawRequest responses: "200": description: Raw request content: text/plain: schema: { type: string } /tokens/{tokenID}/requests/{requestID}/files/{fileID}: parameters: - $ref: "#/components/parameters/TokenID" - $ref: "#/components/parameters/RequestID" - { name: fileID, in: path, required: true, schema: { type: string } } get: summary: Download a request's attached file operationId: downloadFile responses: "200": description: File blob content: application/octet-stream: schema: { type: string, format: binary } "404": { description: Not found } components: parameters: TokenID: name: tokenID in: path required: true description: Token UUID or alias schema: { type: string } RequestID: name: requestID in: path required: true schema: { type: string } schemas: TokenInput: type: object properties: alias: { type: string } default_status: { type: integer, default: 200 } default_content: { type: string } default_content_type: { type: string, default: text/plain } timeout: { type: integer, description: "drives the 100/timeout rpm rate limit; 0 = unlimited" } cors: { type: boolean } expiry: { type: integer, description: "TTL seconds from creation; 0 = never" } actions: { type: boolean } request_limit: { type: integer, description: "max stored requests; 0 = unlimited" } description: { type: string } listen: { type: integer } redirect: { type: string } group_id: { type: string } clone_from: { type: string, description: "copy settings from an existing token UUID" } Token: type: object properties: uuid: { type: string } alias: { type: string } url: { type: string, description: "copyable capture URL" } default_status: { type: integer } default_content: { type: string } default_content_type: { type: string } timeout: { type: integer } cors: { type: boolean } expiry: { type: integer } actions: { type: boolean } request_limit: { type: integer } description: { type: string } listen: { type: integer } redirect: { type: string } group_id: { type: string } premium: { type: boolean } has_password: { type: boolean } created_at: { type: string, format: date-time } updated_at: { type: string, format: date-time } latest_request_at: { type: string, format: date-time, nullable: true } Request: type: object properties: uuid: { type: string } token_id: { type: string } type: { type: string, enum: [web, email, dns] } method: { type: string } ip: { type: string } hostname: { type: string } user_agent: { type: string } content: { type: string } query: { type: object, additionalProperties: { type: array, items: { type: string } } } headers: { type: object, additionalProperties: { type: array, items: { type: string } } } url: { type: string } size: { type: integer } sorting: { type: integer, format: int64 } files: type: array items: { $ref: "#/components/schemas/File" } created_at: { type: string, format: date-time } # Email-specific fields (type == email) sender: { type: string } message_id: { type: string } destinations: { type: string } subject: { type: string } text_content: { type: string } checks: type: object description: "DKIM/SPF/DMARC results, e.g. {\"dkim\":\"pass\",\"spf\":\"fail\"}" additionalProperties: { type: string } File: type: object properties: id: { type: string } request_id: { type: string } filename: { type: string } content_type: { type: string } size: { type: integer } Group: type: object properties: id: { type: string } name: { type: string } color: { type: string } created_at: { type: string, format: date-time } ActionInput: type: object required: [type] properties: type: type: string description: "set_variable | modify_response | conditions | extract_json | extract_regex | http_request | script | dont_save | stop" name: { type: string } position: { type: integer } disabled: { type: boolean } parameters: { type: object, description: "type-specific parameters" } Action: allOf: - $ref: "#/components/schemas/ActionInput" - type: object properties: uuid: { type: string } token_id: { type: string } created_at: { type: string, format: date-time } updated_at: { type: string, format: date-time } ActionRun: type: object properties: id: { type: string } request_id: { type: string } action_id: { type: string } action_type: { type: string } action_name: { type: string } position: { type: integer } output: { type: string } error: { type: string } created_at: { type: string, format: date-time } ScheduleInput: type: object required: [cron] properties: token_id: { type: string } name: { type: string } cron: { type: string, description: "5-field cron expression" } target_url: { type: string } method: { type: string } body: { type: string } run_actions: { type: boolean, description: "run the token's action chain instead of an HTTP check" } expect_status: { type: integer, description: "0 = any 2xx is OK" } keyword: { type: string, description: "alert if the response body lacks this" } check_ssl: { type: boolean } ssl_days: { type: integer } notify_url: { type: string, description: "Shoutrrr alert URL; write-only, never returned" } enabled: { type: boolean } Schedule: type: object properties: uuid: { type: string } token_id: { type: string } name: { type: string } cron: { type: string } target_url: { type: string } method: { type: string } body: { type: string } run_actions: { type: boolean } expect_status: { type: integer } keyword: { type: string } check_ssl: { type: boolean } ssl_days: { type: integer } has_notify: { type: boolean, description: "whether an alert URL is configured (masked)" } enabled: { type: boolean } last_run: { type: string, format: date-time, nullable: true } next_run: { type: string, format: date-time, nullable: true } last_status: { type: string, enum: [ok, alert, error] } last_message: { type: string } created_at: { type: string, format: date-time } updated_at: { type: string, format: date-time } ScheduleRun: type: object properties: id: { type: string } schedule_id: { type: string } status: { type: string, enum: [ok, alert, error] } status_code: { type: integer } message: { type: string } duration_ms: { type: integer } created_at: { type: string, format: date-time } Credentials: type: object required: [email, password] properties: email: { type: string } password: { type: string } User: type: object properties: id: { type: string } email: { type: string } role: { type: string, enum: [admin, user] } created_at: { type: string, format: date-time } updated_at: { type: string, format: date-time } APIKey: type: object properties: id: { type: string } user_id: { type: string } name: { type: string } last_used_at: { type: string, format: date-time, nullable: true } created_at: { type: string, format: date-time } securitySchemes: ApiKey: type: apiKey in: header name: Api-Key