{
  "openapi": "3.1.0",
  "info": {
    "title": "SendSecret API",
    "summary": "End-to-end encrypted, self-expiring secret sharing",
    "description": "Zero-knowledge secret sharing. Clients encrypt locally with AES-256-GCM and only ever send ciphertext. The encryption key travels in the URL fragment and is never transmitted to the server. v2 envelope: encryptedData = \"ss2.\" + base64url(IV ++ ciphertext ++ tag), keys are 43-char base64url in the URL fragment; legacy base64/hex links remain valid. Full crypto recipe and examples: https://sendsecret.io/llms.txt",
    "version": "2.0.0",
    "contact": {
      "email": "security@sendsecret.io"
    },
    "license": {
      "name": "See repository"
    }
  },
  "servers": [
    {
      "url": "https://sendsecret.io"
    }
  ],
  "paths": {
    "/api": {
      "get": {
        "operationId": "getIndex",
        "summary": "Machine-readable API index",
        "responses": {
          "200": {
            "description": "API index with endpoint descriptions and doc links",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiIndexResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/secrets": {
      "post": {
        "operationId": "createSecret",
        "summary": "Create a secret",
        "description": "Stores base64(IV ++ ciphertext ++ GCM tag). Never send plaintext or keys. Returns the id; the share link is https://sendsecret.io/{id}#{key-hex}. Limits: decoded ciphertext <= 256 KB, 5 creates/min/IP.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateSecretRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Secret created",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CreateSecretResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/api/secrets/{id}": {
      "get": {
        "operationId": "getSecret",
        "summary": "Read a secret",
        "description": "Returns the stored ciphertext plus lifetime metadata. Repeatable until expiry (7 days) or deletion. Decrypt locally: first 12 bytes of the decoded blob are the IV, last 16 are the GCM tag. 20 reads/min/IP.",
        "parameters": [
          {
            "$ref": "#/components/parameters/SecretId"
          }
        ],
        "responses": {
          "200": {
            "description": "The encrypted secret",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GetSecretResponse"
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/Error"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "delete": {
        "operationId": "deleteSecret",
        "summary": "Delete a secret immediately",
        "description": "Requires proof of possession of the encryption key: the SHA-256 hex digest of the key. Knowing the id alone is not enough.",
        "parameters": [
          {
            "$ref": "#/components/parameters/SecretId"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/DeleteSecretRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Deleted",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DeleteSecretResponse"
                }
              }
            }
          },
          "403": {
            "$ref": "#/components/responses/Error"
          },
          "404": {
            "$ref": "#/components/responses/Error"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    }
  },
  "components": {
    "parameters": {
      "SecretId": {
        "name": "id",
        "in": "path",
        "required": true,
        "description": "Secret id (8 or 12 alphanumeric characters)",
        "schema": {
          "type": "string",
          "pattern": "^[a-zA-Z0-9]{8,12}$"
        }
      }
    },
    "schemas": {
      "CreateSecretRequest": {
        "type": "object",
        "required": ["encryptedData"],
        "properties": {
          "encryptedData": {
            "type": "string",
            "contentEncoding": "base64",
            "description": "\"ss2.\" + base64url( IV(12 bytes) ++ ciphertext ++ GCM tag(16 bytes) ), unpadded \u2014 or legacy base64 without the prefix. Decoded size <= 256 KB either way.",
            "pattern": "^(ss2\\.[A-Za-z0-9_-]+|[A-Za-z0-9+/]*={0,2})$"
          },
          "deleteToken": {
            "type": "string",
            "pattern": "^[a-f0-9]{64}$",
            "description": "Optional. SHA-256 hex of the key per the key-format scheme (v2: sha256(\"sendsecret-delete-v2:\" + key); legacy: sha256(key string)); required later as the delete proof."
          }
        }
      },
      "CreateSecretResponse": {
        "type": "object",
        "required": ["id", "readModel"],
        "properties": {
          "id": {
            "type": "string",
            "pattern": "^[a-zA-Z0-9]{8,12}$"
          },
          "readModel": {
            "type": "string",
            "const": "repeatable-read until expiry or deletion"
          }
        }
      },
      "GetSecretResponse": {
        "type": "object",
        "required": [
          "id",
          "encryptedData",
          "createdAt",
          "expiresAt",
          "readModel"
        ],
        "properties": {
          "id": {
            "type": "string",
            "pattern": "^[a-zA-Z0-9]{8,12}$"
          },
          "encryptedData": {
            "type": "string",
            "contentEncoding": "base64",
            "description": "\"ss2.\" + base64url( IV ++ ciphertext ++ tag ) \u2014 or legacy base64 without the prefix.",
            "pattern": "^(ss2\\.[A-Za-z0-9_-]+|[A-Za-z0-9+/]*={0,2})$"
          },
          "createdAt": {
            "type": "number",
            "description": "Unix seconds"
          },
          "expiresAt": {
            "type": "number",
            "description": "Unix seconds"
          },
          "readModel": {
            "type": "string",
            "const": "repeatable-read until expiry or deletion"
          }
        }
      },
      "DeleteSecretRequest": {
        "type": "object",
        "required": ["proof"],
        "properties": {
          "proof": {
            "type": "string",
            "pattern": "^[a-f0-9]{64}$",
            "description": "SHA-256 hex of the key-hex string treated as UTF-8 text (sha256 of the 64-char lowercase key string, not of the raw key bytes)."
          }
        }
      },
      "DeleteSecretResponse": {
        "type": "object",
        "required": ["success"],
        "properties": {
          "success": {
            "type": "boolean",
            "const": true
          }
        }
      },
      "ApiIndexResponse": {
        "type": "object",
        "required": ["name", "description", "docs", "openapi", "endpoints"],
        "properties": {
          "name": {
            "type": "string"
          },
          "description": {
            "type": "string"
          },
          "docs": {
            "type": "string",
            "format": "uri"
          },
          "openapi": {
            "type": "string",
            "format": "uri"
          },
          "endpoints": {
            "type": "array",
            "items": {
              "type": "object",
              "required": ["method", "path", "description"],
              "properties": {
                "method": {
                  "type": "string"
                },
                "path": {
                  "type": "string"
                },
                "description": {
                  "type": "string"
                }
              }
            }
          }
        }
      },
      "ApiErrorResponse": {
        "type": "object",
        "required": ["error"],
        "properties": {
          "error": {
            "type": "string"
          }
        }
      }
    },
    "responses": {
      "Error": {
        "description": "Error",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ApiErrorResponse"
            }
          }
        }
      },
      "RateLimited": {
        "description": "Rate limited. Honor Retry-After.",
        "headers": {
          "Retry-After": {
            "schema": {
              "type": "string"
            }
          },
          "X-RateLimit-Reset": {
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ApiErrorResponse"
            }
          }
        }
      }
    }
  }
}
