{
  "openapi": "3.1.0",
  "info": {
    "title": "zpe-paste API",
    "version": "4.0.1",
    "description": "One-time and expiring pastes on zpe.vc/paste, encrypted in the browser and read by whoever holds the link. A paste is read by its 128-bit id · the capability the link carries · and its decryption key never leaves the client; creating and listing are scoped to the account behind the door's session or a key minted for it. Every operation lives under the versioned base `/_/api/v1`, and this document is served at `/_/api/v1/openapi.json`. The unversioned `/api/...` address of an operation answers 308 at its successor."
  },
  "servers": [
    {
      "url": "/paste",
      "description": "zpe-paste on zpe.vc"
    }
  ],
  "paths": {
    "/_/api/v1/me": {
      "get": {
        "operationId": "getPasteMe",
        "summary": "GET /_/api/v1/me",
        "tags": [
          "me"
        ],
        "responses": {
          "200": {
            "description": "Who is asking, as the door sees them. A stranger is a 200 with `signedIn: false` and a sign-in URL, not a 401: the question has an answer whoever asks it.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "signedIn",
                    "accountId",
                    "rung",
                    "email",
                    "tenant",
                    "signin"
                  ],
                  "properties": {
                    "signedIn": {
                      "type": "boolean"
                    },
                    "accountId": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "description": "The account behind the session, or null for a stranger."
                    },
                    "rung": {
                      "type": "integer",
                      "description": "The door's ladder: 0 anonymous, 1 verified, 2 account, 3 workspace, 4 organisation."
                    },
                    "email": {
                      "type": [
                        "string",
                        "null"
                      ]
                    },
                    "tenant": {
                      "type": [
                        "string",
                        "null"
                      ]
                    },
                    "signin": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "description": "Where a stranger signs in; null when signed in."
                    }
                  }
                }
              }
            }
          },
          "500": {
            "description": "This app failed while answering",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "503": {
            "description": "The door (auth.zpe.vc) did not answer, so whether this visitor is signed in could not be checked. Retry once the door is back; nothing about the visitor is implied by this answer.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        }
      }
    },
    "/_/api/v1/paste": {
      "post": {
        "operationId": "postPaste",
        "summary": "POST /_/api/v1/paste",
        "tags": [
          "paste"
        ],
        "responses": {
          "201": {
            "description": "The paste was written. The id is the capability the link carries; the deletion token is answered once and never again.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "id",
                    "deletetoken"
                  ],
                  "properties": {
                    "id": {
                      "type": "string",
                      "description": "The 128-bit id the paste's link carries, and the capability that reads it."
                    },
                    "deletetoken": {
                      "type": "string",
                      "description": "The token that deletes the paste. Answered once, at creation, and never again."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "The request could not be read",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "401": {
            "description": "The credential is missing or does not open this account",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "403": {
            "description": "This account may not do that",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "404": {
            "description": "Nothing is here",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "413": {
            "description": "The body is larger than this deployment accepts (`MAX_BODY_BYTES`, 4 MiB by default).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "415": {
            "description": "The body is not `application/json`, and this path reads nothing else.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/_/api/v1/guest/paste": {
      "post": {
        "operationId": "createGuestPaste",
        "summary": "POST /_/api/v1/guest/paste",
        "tags": [
          "guest"
        ],
        "responses": {
          "201": {
            "description": "The paste was written for a reserved guest principal. Nothing lists it and nothing signs in to it: the delete token is the only handle on the row.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "id",
                    "deletetoken"
                  ],
                  "properties": {
                    "id": {
                      "type": "string",
                      "description": "The 128-bit id the paste's link carries, and the capability that reads it."
                    },
                    "deletetoken": {
                      "type": "string",
                      "description": "The token that deletes the paste. Answered once, at creation, and never again."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "A lifetime longer than this path allows, which is 24 hours.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "401": {
            "description": "The credential is missing or does not open this account",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "403": {
            "description": "This deployment does not take pastes from strangers: the operation exists and is not open here.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "404": {
            "description": "Nothing is here",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "413": {
            "description": "The body is larger than this deployment accepts.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "415": {
            "description": "The body is not `application/json`.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "429": {
            "description": "More pastes than this address may make in a minute. Signing in lifts the limit, because an account is an identity and an address is not.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        }
      }
    },
    "/_/api/v1/pastes": {
      "get": {
        "operationId": "getPastes",
        "summary": "GET /_/api/v1/pastes",
        "tags": [
          "pastes"
        ],
        "responses": {
          "200": {
            "description": "The pastes filed under the caller's account, newest first, each with the state it is in.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "pastes"
                  ],
                  "properties": {
                    "pastes": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "required": [
                          "id",
                          "burn",
                          "created",
                          "expires",
                          "consumedAt",
                          "state"
                        ],
                        "properties": {
                          "id": {
                            "type": "string"
                          },
                          "burn": {
                            "type": "boolean",
                            "description": "One-time: its first read spends it."
                          },
                          "created": {
                            "type": "integer"
                          },
                          "expires": {
                            "type": [
                              "integer",
                              "null"
                            ],
                            "description": "Null when the paste never expires."
                          },
                          "consumedAt": {
                            "type": [
                              "integer",
                              "null"
                            ]
                          },
                          "state": {
                            "type": "string",
                            "enum": [
                              "live",
                              "burned",
                              "expired"
                            ]
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "No credential was presented, or the one presented does not open this account. Sign in at the door, or mint a key for it.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "403": {
            "description": "The credential is real and may not read this app: a key not granted it, or a session the door could not map to an account.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "429": {
            "description": "The key has spent its request ceiling. `retry-after` says when to try again; signing in raises the ceiling, because an account is an identity and a clock is not.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "500": {
            "description": "This app failed while answering",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "503": {
            "description": "The door (auth.zpe.vc) did not answer, so the credential could not be checked. Retry once the door is back: a 503 withholds the verdict rather than refusing the caller.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/_/api/v1/paste/{id}": {
      "get": {
        "operationId": "getPasteById",
        "summary": "GET /_/api/v1/paste/:id",
        "tags": [
          "paste"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "The paste's id · the capability the link carries.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "meta",
            "in": "query",
            "description": "Answer only the paste's head (its metadata), never the ciphertext · what a preview does with an id it merely learned.",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "1"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The paste in the format its client wrote · or only its head when it is one-time, because an ambient GET (a prefetch, a link scanner) must not spend a paste it merely learned the id of.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "v",
                    "wk",
                    "adata",
                    "meta"
                  ],
                  "properties": {
                    "v": {
                      "type": "integer",
                      "description": "The format version the client wrote."
                    },
                    "ct": {
                      "type": "string",
                      "description": "The ciphertext, base64. Absent on a one-time paste's head."
                    },
                    "wk": {
                      "type": "string",
                      "description": "The content key, wrapped to the key in the link's fragment."
                    },
                    "adata": {
                      "type": "object",
                      "description": "The associated data the client authenticated: `fmt` and `bar`."
                    },
                    "meta": {
                      "type": "object",
                      "required": [
                        "expire",
                        "created"
                      ],
                      "properties": {
                        "expire": {
                          "type": "string",
                          "description": "The lifetime the writer chose."
                        },
                        "created": {
                          "type": "integer",
                          "description": "When the paste was written, in seconds since the epoch."
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "The request could not be read",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "401": {
            "description": "The credential is missing or does not open this account",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "403": {
            "description": "This account may not do that",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "404": {
            "description": "Nothing is here",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "410": {
            "description": "A one-time paste whose id has already been read, or that has expired: the id is the capability, and it is spent. Nothing else will ever answer at this address.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        }
      },
      "delete": {
        "operationId": "deletePasteById",
        "summary": "DELETE /_/api/v1/paste/:id",
        "tags": [
          "paste"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "The paste's id.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Delete-Token",
            "in": "header",
            "description": "The deletion token the create answered. Absent, the owner's own session is what allows the delete, and another account's row is refused.",
            "required": false,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The row is gone. The deletion token or the owner's session allowed it, and the answer names what it removed.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "status",
                    "id"
                  ],
                  "properties": {
                    "status": {
                      "type": "string",
                      "enum": [
                        "deleted"
                      ]
                    },
                    "id": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Neither `X-Delete-Token` nor a signed-in session was presented, so there is nothing that could authorise the delete.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "403": {
            "description": "The delete token does not match this row (`wrong-delete-token`), or the row belongs to another account. The paste was not deleted.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "404": {
            "description": "That id is not a paste this app has: never issued, already deleted, or expired.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "500": {
            "description": "This app failed while answering",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "503": {
            "description": "The door (auth.zpe.vc) did not answer, so the owner's session could not be read. Retry once the door is back; the row is untouched.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        }
      }
    },
    "/_/api/v1/paste/{id}/consume": {
      "post": {
        "operationId": "postPasteByIdConsume",
        "summary": "POST /_/api/v1/paste/:id/consume",
        "tags": [
          "paste"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "The one-time paste's id.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Burn-Intent",
            "in": "header",
            "description": "Must be `consume`. Required so a cross-site page cannot burn a paste by linking to it · the answer to a request without it is `400 bad-request`.",
            "required": true,
            "schema": {
              "type": "string",
              "enum": [
                "consume"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The ciphertext, to the one caller whose statement won the row. `X-Burn-Intent: consume` is required, so a cross-site page cannot burn a paste by linking to it.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "v",
                    "wk",
                    "adata",
                    "meta"
                  ],
                  "properties": {
                    "v": {
                      "type": "integer",
                      "description": "The format version the client wrote."
                    },
                    "ct": {
                      "type": "string",
                      "description": "The ciphertext, base64. Absent on a one-time paste's head."
                    },
                    "wk": {
                      "type": "string",
                      "description": "The content key, wrapped to the key in the link's fragment."
                    },
                    "adata": {
                      "type": "object",
                      "description": "The associated data the client authenticated: `fmt` and `bar`."
                    },
                    "meta": {
                      "type": "object",
                      "required": [
                        "expire",
                        "created"
                      ],
                      "properties": {
                        "expire": {
                          "type": "string",
                          "description": "The lifetime the writer chose."
                        },
                        "created": {
                          "type": "integer",
                          "description": "When the paste was written, in seconds since the epoch."
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "The request could not be read",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "401": {
            "description": "The credential is missing or does not open this account",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "403": {
            "description": "This account may not do that",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "404": {
            "description": "Nothing is here",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "410": {
            "description": "Someone else consumed it first, or it has expired. The row is gone and no second read exists.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "description": "An API key minted on the account's settings page at zpe.vc/home/settings. Send it as `Authorization: Bearer <key>`."
      }
    },
    "schemas": {
      "Problem": {
        "type": "object",
        "description": "RFC 9457 problem+json: `type`, `title`, `status`, `code` and `instance`, plus `detail` when a person should read a sentence.",
        "required": [
          "type",
          "title",
          "status",
          "code",
          "instance"
        ],
        "properties": {
          "type": {
            "type": "string",
            "format": "uri"
          },
          "title": {
            "type": "string"
          },
          "status": {
            "type": "integer"
          },
          "code": {
            "type": "string",
            "enum": [
              "bad-request",
              "unauthenticated",
              "malformed",
              "unknown",
              "mismatch",
              "revoked",
              "expired",
              "another-account",
              "scope-refused",
              "grant-refused",
              "forbidden",
              "not-found",
              "method-not-allowed",
              "conflict",
              "gone",
              "payload-too-large",
              "unsupported-media-type",
              "unprocessable",
              "rate-limited",
              "payment-required",
              "not-open",
              "internal",
              "unavailable"
            ]
          },
          "instance": {
            "type": "string"
          },
          "detail": {
            "type": "string"
          }
        }
      }
    }
  },
  "x-surface": {
    "app": "zpe-paste",
    "base": "/_/api/v1",
    "document": "/_/api/v1/openapi.json",
    "generated_by": "zpe-kit machineDocument"
  },
  "x-zpe-routes": 7,
  "x-zpe-documented": 7,
  "x-zpe-internal": []
}
