{
  "info": {
    "_postman_id": "8f1e6f2a-2b7a-4a7c-9f2e-6e6c5a9b7e10",
    "name": "EasyToPaste API",
    "description": "Zero-knowledge secrets API for EasyToPaste. Content is encrypted client-side before it is sent to any `create` endpoint — the server only ever stores ciphertext and non-sensitive envelope metadata (locator, size, type, expiry, view count). See https://easytopaste.com/developers for the full reference, https://api.easytopaste.com/docs for the interactive Swagger UI, and https://api.easytopaste.com/openapi.json for the raw OpenAPI 3.1 spec.\n\nSet the `token` collection variable to an `accessToken` from the Login request to use authenticated endpoints (List Secrets, Revoke Secret, Authorize Recipient, Logout, MFA, Me, Delete Account, Data Export). Anonymous endpoints (Create/Retrieve/Burn Secret, Code Lookup, Abuse Report, Signup, Login, Refresh, JWKS) work with an empty token.",
    "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json"
  },
  "auth": {
    "type": "bearer",
    "bearer": [
      { "key": "token", "value": "{{token}}", "type": "string" }
    ]
  },
  "variable": [
    { "key": "baseUrl", "value": "https://api.easytopaste.com", "type": "string" },
    { "key": "token", "value": "", "type": "string" },
    { "key": "locator", "value": "AbCd12Ef", "type": "string" }
  ],
  "item": [
    {
      "name": "Secrets",
      "item": [
        {
          "name": "Create Secret",
          "request": {
            "method": "POST",
            "header": [
              { "key": "Content-Type", "value": "application/json" },
              { "key": "Idempotency-Key", "value": "{{$guid}}" }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"type\": \"text\",\n  \"mode\": \"link\",\n  \"envelopeVersion\": 1,\n  \"sizeBytes\": 42,\n  \"oneTime\": false,\n  \"expiresInDays\": 1,\n  \"requiresRecipientLogin\": false,\n  \"mimeHint\": \"text/plain\",\n  \"ciphertext\": \"BASE64URL_ENCRYPTED_PAYLOAD\"\n}",
              "options": { "raw": { "language": "json" } }
            },
            "url": {
              "raw": "{{baseUrl}}/v1/secrets",
              "host": ["{{baseUrl}}"],
              "path": ["v1", "secrets"]
            },
            "description": "Create a zero-knowledge secret. Exactly one of `ciphertext` (inline, <=256KB base64url), `uploadId` (from Init Upload), or `chunks` (multi-chunk manifest) must be present. Works anonymously (10/day cap) or signed in (50/day cap on the free tier). Supports Idempotency-Key for safe retries."
          },
          "response": []
        },
        {
          "name": "Retrieve Secret",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/v1/secrets/{{locator}}",
              "host": ["{{baseUrl}}"],
              "path": ["v1", "secrets", "{{locator}}"]
            },
            "description": "Return envelope metadata plus the ciphertext (inline `ciphertext`, a presigned `downloadUrl`, or a `chunks` manifest for multi-chunk files). Does not commit a burn for one-time secrets — call Burn Secret separately after the ciphertext has been fetched and decrypted."
          },
          "response": []
        },
        {
          "name": "Retrieve Secret Chunks",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/v1/secrets/{{locator}}/chunks",
              "host": ["{{baseUrl}}"],
              "path": ["v1", "secrets", "{{locator}}", "chunks"]
            },
            "description": "Return the ordered chunk manifest (5-minute presigned download URLs) for a multi-chunk file secret."
          },
          "response": []
        },
        {
          "name": "Burn Secret",
          "request": {
            "method": "POST",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/v1/secrets/{{locator}}/burn",
              "host": ["{{baseUrl}}"],
              "path": ["v1", "secrets", "{{locator}}", "burn"]
            },
            "description": "Atomically commit burn-after-read for a one-time secret. Exactly one concurrent caller wins (`{ \"burned\": true }`); every other caller gets 410 already_accessed."
          },
          "response": []
        },
        {
          "name": "Revoke Secret",
          "request": {
            "method": "POST",
            "auth": {
              "type": "bearer",
              "bearer": [{ "key": "token", "value": "{{token}}", "type": "string" }]
            },
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/v1/secrets/{{locator}}/revoke",
              "host": ["{{baseUrl}}"],
              "path": ["v1", "secrets", "{{locator}}", "revoke"]
            },
            "description": "Owner-initiated revocation before natural expiry. Requires authentication; only the secret's owner (or an org admin) may revoke."
          },
          "response": []
        },
        {
          "name": "Authorize Recipient",
          "request": {
            "method": "POST",
            "auth": {
              "type": "bearer",
              "bearer": [{ "key": "token", "value": "{{token}}", "type": "string" }]
            },
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/v1/secrets/{{locator}}/authorize",
              "host": ["{{baseUrl}}"],
              "path": ["v1", "secrets", "{{locator}}", "authorize"]
            },
            "description": "For secrets created with requiresRecipientLogin=true. Exchanges the caller's session for a short-lived (60s) recipient bearer token scoped to this locator, used to satisfy the recipient-auth gate on Retrieve Secret. Requires authentication."
          },
          "response": []
        },
        {
          "name": "List Secrets",
          "request": {
            "method": "GET",
            "auth": {
              "type": "bearer",
              "bearer": [{ "key": "token", "value": "{{token}}", "type": "string" }]
            },
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/v1/secrets?limit=50",
              "host": ["{{baseUrl}}"],
              "path": ["v1", "secrets"],
              "query": [
                { "key": "limit", "value": "50" },
                { "key": "cursor", "value": "", "disabled": true }
              ]
            },
            "description": "Cursor-paginated list of the caller's own secrets (metadata only). Requires authentication."
          },
          "response": []
        },
        {
          "name": "Init Upload",
          "request": {
            "method": "POST",
            "header": [
              { "key": "Content-Type", "value": "application/json" },
              { "key": "Idempotency-Key", "value": "{{$guid}}" }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"sizeBytes\": 5242880,\n  \"chunkCount\": 1\n}",
              "options": { "raw": { "language": "json" } }
            },
            "url": {
              "raw": "{{baseUrl}}/v1/secrets/uploads",
              "host": ["{{baseUrl}}"],
              "path": ["v1", "secrets", "uploads"]
            },
            "description": "Mint presigned S3 PUT URLs for ciphertext too large for the inline 256KB limit. Upload each encrypted chunk directly to the returned putUrl(s), then commit with Create Secret using the returned uploadId(s) in `chunks[]`."
          },
          "response": []
        },
        {
          "name": "Code Lookup",
          "request": {
            "method": "POST",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/v1/secrets/code/ABC123/lookup",
              "host": ["{{baseUrl}}"],
              "path": ["v1", "secrets", "code", "ABC123", "lookup"]
            },
            "description": "Resolve a 6-character retrieval code (Mode B / passphrase secrets) to its locator and Argon2id salt/params. Returns no ciphertext or decryption verdict. Rate-limited to 3/min per IP; a code locks out for 15 minutes after 5 failed lookups within 5 minutes."
          },
          "response": []
        }
      ]
    },
    {
      "name": "Identity",
      "item": [
        {
          "name": "Signup",
          "request": {
            "method": "POST",
            "auth": { "type": "noauth" },
            "header": [{ "key": "Content-Type", "value": "application/json" }],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"email\": \"you@example.com\",\n  \"password\": \"a-strong-password-12+\",\n  \"acknowledgedIrreversibility\": true,\n  \"locale\": \"en\"\n}",
              "options": { "raw": { "language": "json" } }
            },
            "url": {
              "raw": "{{baseUrl}}/v1/identity/signup",
              "host": ["{{baseUrl}}"],
              "path": ["v1", "identity", "signup"]
            },
            "description": "Create an email/password account. acknowledgedIrreversibility must be true (secrets cannot be recovered if the link/passphrase is lost). Password must be >=12 characters and not a common password. Rate-limited to 3/IP/hour."
          },
          "response": []
        },
        {
          "name": "Verify Email",
          "request": {
            "method": "POST",
            "auth": { "type": "noauth" },
            "header": [{ "key": "Content-Type", "value": "application/json" }],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"token\": \"EMAIL_VERIFICATION_TOKEN\"\n}",
              "options": { "raw": { "language": "json" } }
            },
            "url": {
              "raw": "{{baseUrl}}/v1/identity/verify-email",
              "host": ["{{baseUrl}}"],
              "path": ["v1", "identity", "verify-email"]
            },
            "description": "Confirm an email address using the one-time token sent after Signup."
          },
          "response": []
        },
        {
          "name": "Login",
          "request": {
            "method": "POST",
            "auth": { "type": "noauth" },
            "header": [{ "key": "Content-Type", "value": "application/json" }],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"email\": \"you@example.com\",\n  \"password\": \"a-strong-password-12+\"\n}",
              "options": { "raw": { "language": "json" } }
            },
            "url": {
              "raw": "{{baseUrl}}/v1/identity/login",
              "host": ["{{baseUrl}}"],
              "path": ["v1", "identity", "login"]
            },
            "description": "Authenticate with email + password (+ optional mfaCode). Returns accessToken (RS256 JWT, 15-minute TTL) and refreshToken (30-day TTL) on success, or { mfaRequired: true } if TOTP is enrolled and mfaCode was omitted. Copy accessToken into the `token` collection variable to use authenticated requests."
          },
          "response": []
        },
        {
          "name": "Refresh Token",
          "request": {
            "method": "POST",
            "auth": { "type": "noauth" },
            "header": [{ "key": "Content-Type", "value": "application/json" }],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"refreshToken\": \"YOUR_REFRESH_TOKEN\"\n}",
              "options": { "raw": { "language": "json" } }
            },
            "url": {
              "raw": "{{baseUrl}}/v1/identity/refresh",
              "host": ["{{baseUrl}}"],
              "path": ["v1", "identity", "refresh"]
            },
            "description": "Exchange a refresh token for a new access + refresh token pair. Refresh tokens are single-use; reuse of an already-rotated token revokes all sessions for the account."
          },
          "response": []
        },
        {
          "name": "Logout",
          "request": {
            "method": "POST",
            "header": [{ "key": "Content-Type", "value": "application/json" }],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"refreshToken\": \"YOUR_REFRESH_TOKEN\"\n}",
              "options": { "raw": { "language": "json" } }
            },
            "url": {
              "raw": "{{baseUrl}}/v1/identity/logout",
              "host": ["{{baseUrl}}"],
              "path": ["v1", "identity", "logout"]
            },
            "description": "Invalidate the current session (denylists the access token's jti) and, if provided, revokes the given refresh token. Requires authentication."
          },
          "response": []
        },
        {
          "name": "Password Reset Request",
          "request": {
            "method": "POST",
            "auth": { "type": "noauth" },
            "header": [{ "key": "Content-Type", "value": "application/json" }],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"email\": \"you@example.com\"\n}",
              "options": { "raw": { "language": "json" } }
            },
            "url": {
              "raw": "{{baseUrl}}/v1/identity/password-reset/request",
              "host": ["{{baseUrl}}"],
              "path": ["v1", "identity", "password-reset", "request"]
            },
            "description": "Initiate a password reset. Always returns 202 to avoid leaking whether the email exists."
          },
          "response": []
        },
        {
          "name": "Password Reset Confirm",
          "request": {
            "method": "POST",
            "auth": { "type": "noauth" },
            "header": [{ "key": "Content-Type", "value": "application/json" }],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"resetToken\": \"PASSWORD_RESET_TOKEN\",\n  \"newPassword\": \"a-new-strong-password-12+\"\n}",
              "options": { "raw": { "language": "json" } }
            },
            "url": {
              "raw": "{{baseUrl}}/v1/identity/password-reset/confirm",
              "host": ["{{baseUrl}}"],
              "path": ["v1", "identity", "password-reset", "confirm"]
            },
            "description": "Complete a password reset with the one-time token. Revokes all active sessions; the caller must log in again (with MFA if enrolled)."
          },
          "response": []
        },
        {
          "name": "MFA Enroll",
          "request": {
            "method": "POST",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/v1/identity/mfa/enroll",
              "host": ["{{baseUrl}}"],
              "path": ["v1", "identity", "mfa", "enroll"]
            },
            "description": "Begin TOTP enrollment. Returns an otpauth URL, a QR code (SVG), and recovery codes. MFA is not active until MFA Verify is called. Requires authentication."
          },
          "response": []
        },
        {
          "name": "MFA Verify",
          "request": {
            "method": "POST",
            "header": [{ "key": "Content-Type", "value": "application/json" }],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"totpCode\": \"123456\",\n  \"currentPassword\": \"a-strong-password-12+\"\n}",
              "options": { "raw": { "language": "json" } }
            },
            "url": {
              "raw": "{{baseUrl}}/v1/identity/mfa/verify",
              "host": ["{{baseUrl}}"],
              "path": ["v1", "identity", "mfa", "verify"]
            },
            "description": "Confirm TOTP enrollment with a fresh code and the current password, activating MFA. Requires authentication."
          },
          "response": []
        },
        {
          "name": "MFA Disenroll",
          "request": {
            "method": "POST",
            "header": [{ "key": "Content-Type", "value": "application/json" }],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"currentPassword\": \"a-strong-password-12+\",\n  \"totpCode\": \"123456\"\n}",
              "options": { "raw": { "language": "json" } }
            },
            "url": {
              "raw": "{{baseUrl}}/v1/identity/mfa/disenroll",
              "host": ["{{baseUrl}}"],
              "path": ["v1", "identity", "mfa", "disenroll"]
            },
            "description": "Remove MFA from the account. Requires both the current password and a valid TOTP code, plus authentication."
          },
          "response": []
        },
        {
          "name": "MFA Recover",
          "request": {
            "method": "POST",
            "auth": { "type": "noauth" },
            "header": [{ "key": "Content-Type", "value": "application/json" }],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"email\": \"you@example.com\",\n  \"password\": \"a-strong-password-12+\",\n  \"recoveryCode\": \"ONE_OF_YOUR_RECOVERY_CODES\"\n}",
              "options": { "raw": { "language": "json" } }
            },
            "url": {
              "raw": "{{baseUrl}}/v1/identity/mfa/recover",
              "host": ["{{baseUrl}}"],
              "path": ["v1", "identity", "mfa", "recover"]
            },
            "description": "Log in with a one-time recovery code in place of a TOTP code. Rate-limited to 3/hour per account; each recovery code is single-use."
          },
          "response": []
        },
        {
          "name": "Get Me",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/v1/identity/me",
              "host": ["{{baseUrl}}"],
              "path": ["v1", "identity", "me"]
            },
            "description": "Return the current authenticated user's profile."
          },
          "response": []
        },
        {
          "name": "JWKS",
          "request": {
            "method": "GET",
            "auth": { "type": "noauth" },
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/v1/identity/.well-known/jwks.json",
              "host": ["{{baseUrl}}"],
              "path": ["v1", "identity", ".well-known", "jwks.json"]
            },
            "description": "Public JSON Web Key Set for verifying EasyToPaste RS256 access tokens (issuer easytopaste.com, audience easytopaste-api). No authentication required. Cacheable for 1 hour."
          },
          "response": []
        },
        {
          "name": "Delete Account",
          "request": {
            "method": "DELETE",
            "header": [{ "key": "Content-Type", "value": "application/json" }],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"confirmation\": \"DELETE\"\n}",
              "options": { "raw": { "language": "json" } }
            },
            "url": {
              "raw": "{{baseUrl}}/v1/identity/account",
              "host": ["{{baseUrl}}"],
              "path": ["v1", "identity", "account"]
            },
            "description": "Schedule account deletion (30-day grace period). Requires the literal confirmation string \"DELETE\", and an mfaCode field if MFA is enrolled. Requires authentication."
          },
          "response": []
        },
        {
          "name": "Data Export",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/v1/identity/data-export",
              "host": ["{{baseUrl}}"],
              "path": ["v1", "identity", "data-export"]
            },
            "description": "Return a data-portability export of the account's profile metadata. Never includes secret ciphertext or decryption keys. Requires authentication."
          },
          "response": []
        }
      ]
    },
    {
      "name": "Abuse",
      "item": [
        {
          "name": "Submit Abuse Report",
          "request": {
            "method": "POST",
            "auth": { "type": "noauth" },
            "header": [{ "key": "Content-Type", "value": "application/json" }],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"locator\": \"{{locator}}\",\n  \"reason_code\": \"other\",\n  \"free_text\": \"Optional description, max 500 chars.\"\n}",
              "options": { "raw": { "language": "json" } }
            },
            "url": {
              "raw": "{{baseUrl}}/v1/abuse-report",
              "host": ["{{baseUrl}}"],
              "path": ["v1", "abuse-report"]
            },
            "description": "Report a secret by its locator for abuse review. Works anonymously. Rate-limited to 5/IP/hour. Returns a reportId case reference."
          },
          "response": []
        }
      ]
    }
  ]
}
