{
  "openapi": "3.1.0",
  "info": {
    "title": "Inboxr API",
    "version": "1",
    "description": "Disposable inboxes for developers, CI, and AI agents — email, SMS, and (soon) voice. Every endpoint takes a Bearer key from https://app.getinboxr.app/api-keys. SMS endpoints additionally require positive credit balance — buy credits at https://app.getinboxr.app/sms/inboxes (Stripe). 1 credit per SMS in or out. For an interactive playground: https://app.getinboxr.app/docs/api.",
    "contact": {
      "url": "https://getinboxr.app"
    },
    "license": {
      "name": "Commercial",
      "url": "https://getinboxr.app/terms"
    }
  },
  "servers": [
    {
      "url": "https://api.getinboxr.app",
      "description": "Production"
    }
  ],
  "tags": [
    {
      "name": "Inboxes",
      "description": "Provision, list, delete, and long-poll email inboxes."
    },
    {
      "name": "Messages",
      "description": "Read inbound mail; send outbound mail."
    },
    {
      "name": "SMS",
      "description": "Phone-number-backed inboxes: send + receive SMS, stake sender claims, long-poll for replies, extract OTPs."
    },
    {
      "name": "Voice",
      "description": "Place outbound calls, drive IVR flows, fetch recordings + voicemail transcripts. (Coming.)"
    }
  ],
  "components": {
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "inb_live_<24 base62>",
        "description": "Generate a key at https://app.getinboxr.app/api-keys. Scopes: read | write | admin (admin ⊃ write ⊃ read)."
      }
    }
  },
  "security": [
    {
      "bearerAuth": []
    }
  ],
  "paths": {
    "/v1/inboxes": {
      "get": {
        "operationId": "list_inboxes",
        "summary": "List your inboxes",
        "description": "Returns every inbox in the calling tenant. Cheap — paginate by ignoring this and using individual GETs if you grow past a few hundred.",
        "tags": [
          "Inboxes"
        ],
        "security": [
          {
            "bearerAuth": [
              "read"
            ]
          }
        ],
        "responses": {
          "200": {
            "description": "{ items: [{ id, address, label, createdAt, expiresAt }] }",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid bearer key."
          },
          "403": {
            "description": "Bearer key lacks the required scope."
          },
          "429": {
            "description": "Plan cap reached."
          }
        }
      },
      "post": {
        "operationId": "create_inbox",
        "summary": "Create a new inbox",
        "description": "Provisions a real disposable address on the engine. Label is optional; if omitted, a random GUID becomes the local-part.",
        "tags": [
          "Inboxes"
        ],
        "security": [
          {
            "bearerAuth": [
              "write"
            ]
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [],
                "properties": {
                  "label": {
                    "description": "Local-part prefix. Lowercase letters, digits, dot/underscore/dash. Max 64.",
                    "type": "string"
                  },
                  "domain": {
                    "description": "Restrict to a domain you have access to. Defaults to getinboxr.app.",
                    "type": "string"
                  },
                  "webhookUrl": {
                    "description": "Optional per-inbox webhook for new mail.",
                    "type": "string"
                  }
                },
                "example": {
                  "label": "signup-test",
                  "domain": "getinboxr.app"
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "{ id, address, label, guid, createdAt, expiresAt }",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid bearer key."
          },
          "403": {
            "description": "Bearer key lacks the required scope."
          },
          "429": {
            "description": "Plan cap reached."
          }
        }
      }
    },
    "/v1/inboxes/{id}": {
      "get": {
        "operationId": "get_inbox",
        "summary": "Get one inbox",
        "description": "Fetch a single inbox by ID. 404 if it does not belong to the calling tenant.",
        "tags": [
          "Inboxes"
        ],
        "security": [
          {
            "bearerAuth": [
              "read"
            ]
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Inbox UUID returned at creation.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "{ id, address, label, createdAt, expiresAt }",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid bearer key."
          },
          "403": {
            "description": "Bearer key lacks the required scope."
          },
          "429": {
            "description": "Plan cap reached."
          }
        }
      },
      "delete": {
        "operationId": "delete_inbox",
        "summary": "Delete an inbox",
        "description": "Removes the mailbox on the engine and clears cached message metadata. Irreversible.",
        "tags": [
          "Inboxes"
        ],
        "security": [
          {
            "bearerAuth": [
              "write"
            ]
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Inbox UUID.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "{ ok: true }",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid bearer key."
          },
          "403": {
            "description": "Bearer key lacks the required scope."
          },
          "429": {
            "description": "Plan cap reached."
          }
        }
      }
    },
    "/v1/inboxes/{id}/smtp-credentials": {
      "post": {
        "operationId": "smtp_credentials",
        "summary": "Issue SMTP submission credentials",
        "description": "Generates a fresh SMTP username + password for this inbox so you can send via smtp.getinboxr.app:587 (STARTTLS) instead of POST /v1/messages. Calling again rotates the credential. Plaintext is shown once — save it.",
        "tags": [
          "Inboxes"
        ],
        "security": [
          {
            "bearerAuth": [
              "admin"
            ]
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Inbox UUID.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "{ host, port, secure, username, password, url, notes }",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid bearer key."
          },
          "403": {
            "description": "Bearer key lacks the required scope."
          },
          "429": {
            "description": "Plan cap reached."
          }
        }
      }
    },
    "/v1/inboxes/{id}/wait": {
      "get": {
        "operationId": "wait_for_mail",
        "summary": "Long-poll for the next message",
        "description": "Blocks up to 30 seconds waiting for a new message in the inbox. Ideal for CI flows that need a verification email and would otherwise busy-poll.",
        "tags": [
          "Inboxes"
        ],
        "security": [
          {
            "bearerAuth": [
              "read"
            ]
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Inbox UUID.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "timeout",
            "in": "query",
            "required": false,
            "description": "Seconds to wait, max 30. Default 30.",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "after",
            "in": "query",
            "required": false,
            "description": "Only return messages received after this ISO timestamp.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "{ message: { uid, from, subject, ... } | null }",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid bearer key."
          },
          "403": {
            "description": "Bearer key lacks the required scope."
          },
          "429": {
            "description": "Plan cap reached."
          }
        }
      }
    },
    "/v1/messages": {
      "get": {
        "operationId": "list_messages",
        "summary": "List recent messages",
        "description": "Tenant-wide inbound mail listing, newest first. Use the inbox filter to narrow down.",
        "tags": [
          "Messages"
        ],
        "security": [
          {
            "bearerAuth": [
              "read"
            ]
          }
        ],
        "parameters": [
          {
            "name": "inbox",
            "in": "query",
            "required": false,
            "description": "Inbox UUID to filter by.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "1..200, default 50.",
            "schema": {
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "{ items: [{ uid, inboxId, from, subject, receivedAt }] }",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid bearer key."
          },
          "403": {
            "description": "Bearer key lacks the required scope."
          },
          "429": {
            "description": "Plan cap reached."
          }
        }
      },
      "post": {
        "operationId": "send_message",
        "summary": "Send an outbound message",
        "description": "Outbound via SES relay. From: must be on a domain enabled on the engine. Recorded in your outbound history regardless of success.",
        "tags": [
          "Messages"
        ],
        "security": [
          {
            "bearerAuth": [
              "write"
            ]
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "from",
                  "to",
                  "subject"
                ],
                "properties": {
                  "from": {
                    "description": "Sender address. Domain must be SES-verified for your tenant.",
                    "type": "string"
                  },
                  "to": {
                    "description": "One address or an array.",
                    "type": "array",
                    "items": {
                      "type": "string"
                    }
                  },
                  "subject": {
                    "description": "Plain text subject.",
                    "type": "string"
                  },
                  "text": {
                    "description": "Plain-text body. Either text or html is required.",
                    "type": "string"
                  },
                  "html": {
                    "description": "HTML body.",
                    "type": "string"
                  },
                  "cc": {
                    "description": "CC list.",
                    "type": "array",
                    "items": {
                      "type": "string"
                    }
                  },
                  "bcc": {
                    "description": "BCC list.",
                    "type": "array",
                    "items": {
                      "type": "string"
                    }
                  }
                },
                "example": {
                  "from": "noreply@getinboxr.app",
                  "to": "someone@example.com",
                  "subject": "Hi from Inboxr",
                  "text": "Test message body."
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "{ ok, outboundId, messageId }",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid bearer key."
          },
          "403": {
            "description": "Bearer key lacks the required scope."
          },
          "429": {
            "description": "Plan cap reached."
          }
        }
      }
    },
    "/v1/messages/{uid}": {
      "get": {
        "operationId": "get_message",
        "summary": "Get one message (full body)",
        "description": "Returns full text + sanitized HTML + attachments metadata. Use the attachment download URL for binaries.",
        "tags": [
          "Messages"
        ],
        "security": [
          {
            "bearerAuth": [
              "read"
            ]
          }
        ],
        "parameters": [
          {
            "name": "uid",
            "in": "path",
            "required": true,
            "description": "Engine UID returned by list_messages.",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "domain",
            "in": "query",
            "required": true,
            "description": "The inbox domain (e.g. getinboxr.app). Required because UIDs are scoped per-mailbox.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "{ uid, from, subject, textBody, htmlBody, attachments: [...] }",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid bearer key."
          },
          "403": {
            "description": "Bearer key lacks the required scope."
          },
          "429": {
            "description": "Plan cap reached."
          }
        }
      }
    },
    "/v1/sms/inboxes": {
      "get": {
        "operationId": "sms_list_inboxes",
        "summary": "List SMS inboxes",
        "description": "Every inbox owns a real Australian mobile number that can send + receive SMS. Created via POST /v1/sms/inboxes. 1 credit per outbound and per inbound.",
        "tags": [
          "SMS"
        ],
        "security": [
          {
            "bearerAuth": [
              "read"
            ]
          }
        ],
        "responses": {
          "200": {
            "description": "{ items: [{ id, phoneNumber, label, deviceId, createdAt }] }",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid bearer key."
          },
          "403": {
            "description": "Bearer key lacks the required scope."
          },
          "429": {
            "description": "Plan cap reached."
          }
        }
      },
      "post": {
        "operationId": "sms_create_inbox",
        "summary": "Create an SMS inbox",
        "description": "Provisions a new SMS inbox bound to the least-loaded online phone in the SmsSaaS pool. Requires positive credit balance — buy credits at app.getinboxr.app/sms/inboxes.",
        "tags": [
          "SMS"
        ],
        "security": [
          {
            "bearerAuth": [
              "write"
            ]
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [],
                "properties": {
                  "label": {
                    "description": "Optional human label, e.g. \"signup-bot\".",
                    "type": "string"
                  }
                },
                "example": {
                  "label": "signup-bot"
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "{ id, phoneNumber, label, deviceId }",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid bearer key."
          },
          "403": {
            "description": "Bearer key lacks the required scope."
          },
          "429": {
            "description": "Plan cap reached."
          }
        }
      }
    },
    "/v1/sms/inboxes/{id}": {
      "delete": {
        "operationId": "sms_delete_inbox",
        "summary": "Delete an SMS inbox",
        "description": "Releases the inbox. Future inbound SMS to that phone number from never-claimed senders becomes \"unclaimed\" in the admin view. Existing message history is preserved on the tenant for 90 days.",
        "tags": [
          "SMS"
        ],
        "security": [
          {
            "bearerAuth": [
              "write"
            ]
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Inbox ID.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "{ ok: true }",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid bearer key."
          },
          "403": {
            "description": "Bearer key lacks the required scope."
          },
          "429": {
            "description": "Plan cap reached."
          }
        }
      }
    },
    "/v1/sms/send": {
      "post": {
        "operationId": "sms_send",
        "summary": "Send an SMS",
        "description": "Sends from the inbox's assigned number. Long bodies are auto-split into multipart messages — credits charge per part. Recipient sees the SIM's MSISDN as the sender (no alphanumeric sender ID with the current architecture; that requires a separate SIP/aggregator add-on).",
        "tags": [
          "SMS"
        ],
        "security": [
          {
            "bearerAuth": [
              "write"
            ]
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "inboxId",
                  "toNumber",
                  "body"
                ],
                "properties": {
                  "inboxId": {
                    "description": "Source inbox ID.",
                    "type": "string"
                  },
                  "toNumber": {
                    "description": "Recipient in E.164, e.g. \"+61413253383\".",
                    "type": "string"
                  },
                  "body": {
                    "description": "Message text. Auto-split if > 160 chars.",
                    "type": "string"
                  }
                },
                "example": {
                  "inboxId": "sms_…",
                  "toNumber": "+61413253383",
                  "body": "Hello from Inboxr"
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "{ id, status: \"pending\"|\"sending\"|\"sent\", direction: \"outbound\", ... }",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid bearer key."
          },
          "403": {
            "description": "Bearer key lacks the required scope."
          },
          "429": {
            "description": "Plan cap reached."
          }
        }
      }
    },
    "/v1/sms/inboxes/{id}/claim": {
      "post": {
        "operationId": "sms_claim_sender",
        "summary": "Claim a sender",
        "description": "Stake a 30-minute claim that the next inbound from senderNumber routes to this inbox even without a prior outbound pairing. Useful for OTP verification when you trigger the SMS from a non-Inboxr channel (e.g. a signup form on another service).",
        "tags": [
          "SMS"
        ],
        "security": [
          {
            "bearerAuth": [
              "write"
            ]
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Inbox ID.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "senderNumber"
                ],
                "properties": {
                  "senderNumber": {
                    "description": "Expected sender, in E.164.",
                    "type": "string"
                  }
                },
                "example": {
                  "senderNumber": "+61400000000"
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "{ ok: true, expiresIn: 1800 }",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid bearer key."
          },
          "403": {
            "description": "Bearer key lacks the required scope."
          },
          "429": {
            "description": "Plan cap reached."
          }
        }
      }
    },
    "/v1/sms/inboxes/{id}/messages": {
      "get": {
        "operationId": "sms_list_messages",
        "summary": "List messages in an SMS inbox",
        "description": "Returns inbound + outbound thread, newest first. Each message has direction, fromNumber, toNumber, body, status, receivedAt.",
        "tags": [
          "SMS"
        ],
        "security": [
          {
            "bearerAuth": [
              "read"
            ]
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Inbox ID.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Default 50, max 200.",
            "schema": {
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "{ items: [{ id, direction, fromNumber, toNumber, body, status, receivedAt }] }",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid bearer key."
          },
          "403": {
            "description": "Bearer key lacks the required scope."
          },
          "429": {
            "description": "Plan cap reached."
          }
        }
      }
    },
    "/v1/sms/inboxes/{id}/messages/wait": {
      "get": {
        "operationId": "sms_wait_for_message",
        "summary": "Long-poll for the next inbound SMS",
        "description": "Blocks up to 30s for a new inbound message. Filter by `from` to wait for a specific sender. Pair with /claim to make sure the sender routes here.",
        "tags": [
          "SMS"
        ],
        "security": [
          {
            "bearerAuth": [
              "read"
            ]
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Inbox ID.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "since",
            "in": "query",
            "required": false,
            "description": "ISO timestamp; only return messages after this. Defaults to now.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "from",
            "in": "query",
            "required": false,
            "description": "Optional E.164 sender filter.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "timeout",
            "in": "query",
            "required": false,
            "description": "Max wait ms (default 30000, max 60000).",
            "schema": {
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "{ message: { id, direction, fromNumber, body, receivedAt } | null }",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid bearer key."
          },
          "403": {
            "description": "Bearer key lacks the required scope."
          },
          "429": {
            "description": "Plan cap reached."
          }
        }
      }
    }
  },
  "externalDocs": {
    "url": "https://getinboxr.app/llms-full.txt",
    "description": "Full markdown docs (LLM-friendly)"
  }
}