{
  "openapi": "3.1.0",
  "info": {
    "title": "Coffrify Public API",
    "version": "1.0.0",
    "summary": "Encrypted file transfers as a service — for developers.",
    "description": "End-to-end encrypted file transfers with webhooks, audit log and SDK in TypeScript.\n\n**Authentication.** All endpoints (except `/v1/welcome`) require `Authorization: Bearer cof_live_…` or `cof_test_…`. Keys are scoped — see `coffrify_api_scopes` for the full list.\n\n**Versioning.** Every response carries `X-Coffrify-Api-Version: 2026-05-14`. Breaking changes ship under a new version with overlap and `Deprecation`/`Sunset` headers.",
    "contact": {
      "name": "Coffrify Support",
      "email": "support@coffrify.com",
      "url": "https://help.coffrify.com"
    },
    "license": {
      "name": "Proprietary",
      "identifier": "Coffrify-1.0"
    },
    "x-coffrify-api-version": "2026-05-14"
  },
  "servers": [
    {
      "url": "https://api.coffrify.com",
      "description": "Production"
    },
    {
      "url": "https://api.coffrify.com/v1",
      "description": "Production (v1 namespace)"
    }
  ],
  "security": [
    {
      "bearerAuth": []
    }
  ],
  "components": {
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "description": "API key. Format: `cof_live_<hex>` (production) or `cof_test_<hex>` (sandbox)."
      }
    },
    "schemas": {
      "Error": {
        "type": "object",
        "required": [
          "error"
        ],
        "properties": {
          "error": {
            "type": "object",
            "required": [
              "code",
              "message"
            ],
            "properties": {
              "code": {
                "type": "string",
                "description": "Machine-readable error code (e.g. `invalid_api_key`)."
              },
              "message": {
                "type": "string"
              },
              "param": {
                "type": "string",
                "nullable": true
              },
              "doc_url": {
                "type": "string",
                "nullable": true
              },
              "request_id": {
                "type": "string",
                "nullable": true
              }
            }
          }
        }
      },
      "Transfer": {
        "type": "object",
        "required": [
          "id",
          "short_code",
          "status",
          "created_at"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "short_code": {
            "type": "string"
          },
          "transfer_title": {
            "type": "string",
            "nullable": true
          },
          "status": {
            "type": "string",
            "enum": [
              "active",
              "expired",
              "deleted",
              "scanning"
            ]
          },
          "expires_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "total_downloads": {
            "type": "integer"
          },
          "max_downloads": {
            "type": "integer",
            "nullable": true
          },
          "scan_status": {
            "type": "string",
            "nullable": true
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "ApiKey": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "name": {
            "type": "string"
          },
          "key_prefix": {
            "type": "string"
          },
          "scopes": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "environment": {
            "type": "string",
            "enum": [
              "live",
              "test"
            ]
          },
          "is_active": {
            "type": "boolean"
          },
          "expires_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "used_count": {
            "type": "integer"
          },
          "last_used_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "revoked_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          }
        }
      },
      "Webhook": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "name": {
            "type": "string"
          },
          "description": {
            "type": "string",
            "nullable": true
          },
          "url": {
            "type": "string",
            "format": "uri"
          },
          "events": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "is_active": {
            "type": "boolean"
          },
          "success_count": {
            "type": "integer"
          },
          "failure_count": {
            "type": "integer"
          },
          "last_triggered_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "last_status": {
            "type": "integer",
            "nullable": true
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "AuditEntry": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "workspace_id": {
            "type": "string",
            "format": "uuid"
          },
          "actor_id": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "action": {
            "type": "string"
          },
          "resource_type": {
            "type": "string",
            "nullable": true
          },
          "resource_id": {
            "type": "string",
            "nullable": true
          },
          "ip_address": {
            "type": "string",
            "nullable": true
          },
          "user_agent": {
            "type": "string",
            "nullable": true
          },
          "country_code": {
            "type": "string",
            "nullable": true
          },
          "status": {
            "type": "string"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "TransferList": {
        "type": "object",
        "required": [
          "object",
          "data",
          "has_more",
          "limit"
        ],
        "properties": {
          "object": {
            "const": "list"
          },
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Transfer"
            }
          },
          "next_cursor": {
            "type": "string",
            "nullable": true,
            "description": "Pass back as `cursor` for the next page."
          },
          "has_more": {
            "type": "boolean"
          },
          "limit": {
            "type": "integer"
          },
          "offset": {
            "type": "integer",
            "nullable": true,
            "description": "Legacy offset/limit pagination."
          }
        }
      },
      "ApiKeyList": {
        "type": "object",
        "required": [
          "object",
          "data",
          "has_more",
          "limit"
        ],
        "properties": {
          "object": {
            "const": "list"
          },
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ApiKey"
            }
          },
          "next_cursor": {
            "type": "string",
            "nullable": true,
            "description": "Pass back as `cursor` for the next page."
          },
          "has_more": {
            "type": "boolean"
          },
          "limit": {
            "type": "integer"
          },
          "offset": {
            "type": "integer",
            "nullable": true,
            "description": "Legacy offset/limit pagination."
          }
        }
      },
      "WebhookList": {
        "type": "object",
        "required": [
          "object",
          "data",
          "has_more",
          "limit"
        ],
        "properties": {
          "object": {
            "const": "list"
          },
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Webhook"
            }
          },
          "next_cursor": {
            "type": "string",
            "nullable": true,
            "description": "Pass back as `cursor` for the next page."
          },
          "has_more": {
            "type": "boolean"
          },
          "limit": {
            "type": "integer"
          },
          "offset": {
            "type": "integer",
            "nullable": true,
            "description": "Legacy offset/limit pagination."
          }
        }
      },
      "AuditEntryList": {
        "type": "object",
        "required": [
          "object",
          "data",
          "has_more",
          "limit"
        ],
        "properties": {
          "object": {
            "const": "list"
          },
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/AuditEntry"
            }
          },
          "next_cursor": {
            "type": "string",
            "nullable": true,
            "description": "Pass back as `cursor` for the next page."
          },
          "has_more": {
            "type": "boolean"
          },
          "limit": {
            "type": "integer"
          },
          "offset": {
            "type": "integer",
            "nullable": true,
            "description": "Legacy offset/limit pagination."
          }
        }
      }
    },
    "parameters": {
      "limit": {
        "name": "limit",
        "in": "query",
        "schema": {
          "type": "integer",
          "minimum": 1,
          "maximum": 100,
          "default": 20
        },
        "description": "Page size. Maximum varies by endpoint (default 20, /v1/audit allows up to 500)."
      },
      "cursor": {
        "name": "cursor",
        "in": "query",
        "schema": {
          "type": "string"
        },
        "description": "Opaque pagination cursor returned by a previous call as `next_cursor`."
      },
      "offset": {
        "name": "offset",
        "in": "query",
        "schema": {
          "type": "integer",
          "minimum": 0,
          "default": 0
        },
        "description": "Legacy offset/limit pagination. Prefer `cursor` for stable paging under concurrent writes."
      }
    },
    "responses": {
      "401": {
        "description": "Unauthorized — missing, invalid, expired or revoked API key.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "403": {
        "description": "Forbidden — required scope not granted, or IP not allowed.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "404": {
        "description": "Not found.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "429": {
        "description": "Rate limit exceeded. See `X-RateLimit-*` and `Retry-After` headers.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "500": {
        "description": "Internal server error. Include `X-Request-Id` in support tickets.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      }
    },
    "headers": {
      "X-RateLimit-Limit": {
        "schema": {
          "type": "integer"
        },
        "description": "Per-minute quota for the workspace + endpoint class."
      },
      "X-RateLimit-Remaining": {
        "schema": {
          "type": "integer"
        },
        "description": "Requests left in the current 60s window."
      },
      "X-RateLimit-Reset": {
        "schema": {
          "type": "integer"
        },
        "description": "Epoch seconds when the current window resets."
      },
      "X-RateLimit-Policy": {
        "schema": {
          "type": "string"
        },
        "description": "RFC 8941-style policy string, e.g. `300;w=60`."
      },
      "Retry-After": {
        "schema": {
          "type": "integer"
        },
        "description": "Only on 429 — seconds until next acceptable request."
      },
      "X-Request-Id": {
        "schema": {
          "type": "string"
        }
      },
      "X-Coffrify-Api-Version": {
        "schema": {
          "type": "string",
          "example": "2026-05-14"
        }
      }
    }
  },
  "tags": [
    {
      "name": "Meta",
      "description": "Welcome + identity endpoints."
    },
    {
      "name": "Transfers",
      "description": "Create, list, inspect and revoke transfers."
    },
    {
      "name": "API keys",
      "description": "Manage your API keys (scopes, rotation, revocation)."
    },
    {
      "name": "Webhooks",
      "description": "Subscribe to events. 43+ event types in the catalog."
    },
    {
      "name": "Audit",
      "description": "Read the workspace audit log."
    }
  ],
  "paths": {
    "/v1/welcome": {
      "get": {
        "tags": [
          "Meta"
        ],
        "summary": "Public welcome message + API metadata.",
        "security": [],
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/v1/me": {
      "get": {
        "tags": [
          "Meta"
        ],
        "summary": "Resolve the API key into its workspace, scopes and user.",
        "responses": {
          "200": {
            "description": "OK"
          },
          "401": {
            "$ref": "#/components/responses/401"
          }
        }
      }
    },
    "/v1/transfers": {
      "get": {
        "tags": [
          "Transfers"
        ],
        "summary": "List transfers in the workspace.",
        "parameters": [
          {
            "$ref": "#/components/parameters/limit"
          },
          {
            "$ref": "#/components/parameters/cursor"
          },
          {
            "$ref": "#/components/parameters/offset"
          },
          {
            "name": "status",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "active",
                "expired",
                "deleted",
                "scanning"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TransferList"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/401"
          },
          "429": {
            "$ref": "#/components/responses/429"
          }
        }
      },
      "post": {
        "tags": [
          "Transfers"
        ],
        "summary": "Create a transfer (returns a presigned upload token).",
        "responses": {
          "201": {
            "description": "Created"
          },
          "401": {
            "$ref": "#/components/responses/401"
          }
        }
      }
    },
    "/v1/transfers/{id}": {
      "parameters": [
        {
          "name": "id",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string",
            "format": "uuid"
          }
        }
      ],
      "get": {
        "tags": [
          "Transfers"
        ],
        "summary": "Fetch a transfer by ID.",
        "responses": {
          "200": {
            "description": "OK"
          },
          "404": {
            "$ref": "#/components/responses/404"
          }
        }
      },
      "delete": {
        "tags": [
          "Transfers"
        ],
        "summary": "Revoke a transfer (delete files).",
        "responses": {
          "200": {
            "description": "OK"
          },
          "404": {
            "$ref": "#/components/responses/404"
          }
        }
      }
    },
    "/v1/api-keys": {
      "get": {
        "tags": [
          "API keys"
        ],
        "summary": "List API keys for the workspace.",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiKeyList"
                }
              }
            }
          }
        }
      },
      "post": {
        "tags": [
          "API keys"
        ],
        "summary": "Create a new API key. Returns the raw key ONCE.",
        "responses": {
          "201": {
            "description": "Created"
          },
          "429": {
            "$ref": "#/components/responses/429"
          }
        }
      }
    },
    "/v1/api-keys/{id}": {
      "parameters": [
        {
          "name": "id",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string",
            "format": "uuid"
          }
        }
      ],
      "patch": {
        "tags": [
          "API keys"
        ],
        "summary": "Update name / IP allowlist / max_uses on a key.",
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      },
      "delete": {
        "tags": [
          "API keys"
        ],
        "summary": "Revoke a key. Fires `api_key.revoked`.",
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/v1/api-keys/{id}/rotate": {
      "parameters": [
        {
          "name": "id",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string",
            "format": "uuid"
          }
        }
      ],
      "post": {
        "tags": [
          "API keys"
        ],
        "summary": "Rotate a key — new value + grace period for the old one.",
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/v1/webhooks": {
      "get": {
        "tags": [
          "Webhooks"
        ],
        "summary": "List webhooks.",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookList"
                }
              }
            }
          }
        }
      },
      "post": {
        "tags": [
          "Webhooks"
        ],
        "summary": "Create a webhook. Returns the signing secret ONCE.",
        "responses": {
          "201": {
            "description": "Created"
          }
        }
      },
      "patch": {
        "tags": [
          "Webhooks"
        ],
        "summary": "Update a webhook.",
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      },
      "delete": {
        "tags": [
          "Webhooks"
        ],
        "summary": "Delete a webhook.",
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/v1/webhooks/events": {
      "get": {
        "tags": [
          "Webhooks"
        ],
        "summary": "List the event catalog (event types + payload shape).",
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/v1/webhooks/{id}/deliveries": {
      "parameters": [
        {
          "name": "id",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string",
            "format": "uuid"
          }
        }
      ],
      "get": {
        "tags": [
          "Webhooks"
        ],
        "summary": "List delivery attempts for a webhook (debug).",
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/v1/audit": {
      "get": {
        "tags": [
          "Audit"
        ],
        "summary": "Query the workspace audit log.",
        "parameters": [
          {
            "$ref": "#/components/parameters/limit"
          },
          {
            "$ref": "#/components/parameters/cursor"
          },
          {
            "$ref": "#/components/parameters/offset"
          },
          {
            "name": "action",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "actor_id",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "resource_type",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "since",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          },
          {
            "name": "until",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AuditEntryList"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/401"
          }
        }
      }
    }
  },
  "x-coffrify": {
    "openapi_version": "1.0.0",
    "api_version": "2026-05-14",
    "generated_at": "2026-05-26T16:10:09.575Z",
    "rate_limit_doc": "https://docs.coffrify.com/api/rate-limits",
    "idempotency_doc": "https://docs.coffrify.com/api/idempotency",
    "event_catalog_endpoint": "/v1/webhooks/events",
    "webhooks": {
      "signing": {
        "standard_compliant": true,
        "spec": "https://www.standardwebhooks.com/",
        "headers_emitted": [
          "webhook-id (uuid — stable across retries; use for idempotency)",
          "webhook-timestamp (unix seconds)",
          "webhook-signature (`v1,<base64-hmac-sha256>` — multi-sig space-separated during rotation)",
          "X-Coffrify-Signature (legacy `t=<ts>,v1=<hex>` format — kept for back-compat)",
          "X-Coffrify-Event-Id, X-Coffrify-Event-Type (legacy headers)",
          "X-Coffrify-Test-Delivery: true (only on /v1/webhooks/{id}/test deliveries)"
        ],
        "idempotency": "Receivers should dedupe on `webhook-id`. The same id is used across all retries AND replays."
      },
      "replay": {
        "endpoint": "POST /v1/webhooks/{id}/test (action=replay via dashboard) — preserves the original event_id.",
        "marker": "Replay deliveries set `original_delivery_id` in the database and ship the same `webhook-id` header."
      },
      "rotation": {
        "endpoint": "POST /v1/webhooks/{id}/rotate-secret",
        "grace_window_default_hours": 24,
        "grace_window_max_hours": 168,
        "behavior": "Both the new and previous secrets validate during the grace window. Plan your rollover; after the window closes, only the new secret works."
      },
      "retry_policy": {
        "default_max_attempts": 10,
        "default_disable_after_consecutive_failures": 50,
        "override": "Set `retry_policy.disable_after_consecutive_failures` per webhook (1–10000).",
        "backoff_seconds": [
          60,
          300,
          1800,
          7200,
          21600,
          43200,
          86400,
          86400,
          86400
        ]
      },
      "sandbox": {
        "endpoint": "POST /v1/webhooks/{id}/test",
        "accepts": {
          "id": "uuid (required)",
          "event_type": "any catalog entry or 'ping' (default)",
          "data": "optional custom payload"
        }
      }
    }
  }
}