# MailSMMO — Gmail OTP API MailSMMO rents real @gmail.com addresses to receive one-time codes (OTP) for a given service (Facebook, TikTok, Instagram, PayPal…). It also sells Hotmail/Outlook accounts outright and reads mailboxes from credentials you supply. This single file is the complete API reference for developers and AI tools (Markdown, English). The Gmail OTP API is the main product and is documented first and in full. ## Pages - [MailSMMO](https://mailsmmo.com/): overview, services and FAQ - [Gmail OTP rental](https://mailsmmo.com/gmail-otp-rental): rent a Gmail address per service and read the OTP - [Pricing](https://mailsmmo.com/pricing): live prices for every service and product - [API docs](https://mailsmmo.com/api-docs): interactive playground for your API key - [Hotmail / Outlook](https://mailsmmo.com/hotmail-outlook), [Domain mail](https://mailsmmo.com/domain-mail) - [Refund policy](https://mailsmmo.com/refund-policy), [Privacy policy](https://mailsmmo.com/privacy-policy), [Terms](https://mailsmmo.com/terms) ## Basics Base URL: `https://mailsmmo.com/api/v1` **Authentication.** Send your API key as the query parameter `?api_key=` (keys look like `rm_live_…`; get yours in the dashboard, section API Docs). This is the only way: there is no API-key header. Changing the account password revokes every key of the account. Two endpoints need no key: `GET /services` and `GET /orders/otp` (the order id itself is the capability to read it). **Response envelope.** Every response is JSON in one of two shapes: ``` { "status": "success", "data": ... } HTTP 200 { "status": "error", "message": "", "retryable": bool } HTTP 4xx/5xx — see Errors ``` Check `status` first. `message` is always a snake_case error code (never a sentence) and `retryable` says whether repeating the same call can succeed. There are no other error fields. **Money.** `price`, `amount` and `balance` are in the same unit as your account balance. There is no per-key rate limit, but every order is paid for, so do not create orders in a loop. ## Gmail OTP: the flow 1. Pick a service: `GET /services`. 2. Create an order: `GET /orders?api_key=…&service=otp_gmail_facebook&unique_key=`. You get a Gmail address and an order `id`. **You are charged now**, when the order is created. 3. Use that Gmail address on the target site to request the code. 4. Poll `GET /orders/otp?order_id=` every ~2 seconds until `otp` is not null (or listen on the WebSocket and then call it). 5. The order lives for a window (10 minutes by default). If no code arrives in time you are refunded automatically (see "Refunds"). Orders cannot be cancelled. ### GET /services — catalogue (no auth) ```json { "status": "success", "data": { "services": [ { "code": "otp_gmail_facebook", "name": "Facebook", "description": "", "price": 1600, "stock": 5231, "brand_color": "#1877f2", "is_active": true, "sort_order": 1, "high_priority": true, "price_high": 1800, "stock_high": 9120 } ] } } ``` - `code`: the service code to order with. The bare form (`facebook`) is accepted too. - `price` / `stock`: normal tier. `stock` is how many can be supplied right now at `price`. - `high_priority`, `price_high`, `stock_high`: the optional priority tier (see below). `price_high` is `null` when the service has no priority tier. - `GET /services?service=otp_gmail_facebook` returns only `{ "price", "stock" }` of that service (normal tier); use the full list to see the priority tier. ### GET /orders — create a Gmail OTP order Parameters: `api_key`, `service` (required), `unique_key` (strongly recommended — a fresh uuid per purchase). `quantity` is ignored for Gmail OTP. ```json { "status": "success", "data": { "id": "3f2b9c1e-8a4d-4c7e-9b1a-2d5e6f7a8b9c", "email": "someone@gmail.com", "service": "otp_gmail_facebook", "amount": 1600 } } ``` - `id`: the order id — a lowercase UUID. Anyone holding it can read that order's OTP; keep it private. - `email`: the Gmail address to use on the target site. - `amount`: what was actually charged (see "Priority tier"). - The call can take up to ~45 seconds while stock is found. Use an HTTP timeout of at least 60 s. **Idempotency (`unique_key`).** Repeating the call with the same `unique_key` within **30 days** returns the original order and never charges twice. After 30 days the key is forgotten and reusing it creates a new, charged order. A key is bound to its request: reusing it for a different service returns 422 `idempotency_mismatch`. Always send a `unique_key`: without one, a transient failure leaves you unable to tell whether an order was created. ### GET /orders/otp — read the code (no auth) Parameters: `order_id` (required), `from` (optional, unix seconds). ```json // no code yet { "status": "success", "data": { "otp": null, "amount": 1600, "received_at": null, "otp_history": [] } } // code arrived { "status": "success", "data": { "otp": "123456", "amount": 1600, "received_at": 1732000000, "from": "security@facebookmail.com", "otp_history": [ { "value": "123456", "received_at": 1732000000 } ] } } ``` - `otp`: the newest code (string) or `null`. `received_at`: unix seconds of that code. - `from`: sender of the mail that carried the code (present only when `otp` is set). - `otp_history`: every code received for this order, newest first (up to 20). - `from=`: returns `otp: null` until a code strictly newer than that time arrives — use it to wait for a second code without re-reading the first. - `amount`: what this order was charged. - `notice`: present only when the code cannot arrive on an open order. `"mail_disabled"` means the mail source disabled the mailbox: stop waiting for this order; it is refunded automatically when it expires (no action needed), so rent a new one. - A code that arrived within the window can still be read after the window ends. Once an order has been refunded it is closed and this endpoint returns 404 `not_found`. ### GET /otp/orders — your orders Parameters: `page` (default 1), `page_size` (default 20, max 200). ```json { "status": "success", "data": { "orders": [ { "id": "3f2b9c1e-…", "email": "someone@gmail.com", "status": "active", "expires_at": "2026-10-22T01:24:22+07:00", "created_at": "2026-10-22T01:14:22+07:00", "service": "otp_gmail_facebook", "bound": true, "latest_otp": "123456" } ], "page": 1, "page_size": 20, "total": 37 } } ``` - `status`: `active` (open or completed) or `released` (refunded / closed without a code). - `expires_at`: end of the window. `bound`: true once a code has arrived (no refund then). - `latest_otp`: newest code, omitted when none. Served straight from the database: a code that arrived a moment ago may show up on a later call. To read a code, use `/orders/otp`. ### Priority tier A service may offer a second, faster tier (`high_priority: true` and `price_high` set). It is an **account setting**, not a per-order parameter: ``` POST /api/v1/me/priority?api_key=... body: {"high": true} → {"otp_high_priority": true} ``` With the setting on, every later order (web or API) uses the priority tier on services that offer it; on other services it has no effect. - Our own Gmail stock is still used first and charged `price`. Only a Gmail bought from an external supplier is charged `price_high`. The response `amount` tells you which. - The search for stock lasts longer, so the create call may take longer. - Your balance must cover `price_high` (it is held while the order is created), otherwise you get 402 `insufficient_balance` even if the final charge would be `price`. ### Refunds You are refunded automatically when an order's window ends without any code: - Gmail from our own stock: refunded within about 30 seconds after `expires_at`. - Gmail bought from an external supplier: refunded automatically once the supplier reports the order expired. An order that received a code is never refunded. The refund appears in your balance (`GET /me`). ### WebSocket (optional, instant notification) Connect to `wss://mailsmmo.com/api/v1/ws` and within 5 seconds send `{"type":"auth","token":""}`. The server answers `{"type":"ready"}`, then sends one frame per new mail on your orders: ```json { "type": "mail", "data": { "kind": "mail", "to": "someone@gmail.com", "has_otp": true, "alias_id": 123, "user_id": 45, "email_id": 678 } } ``` The frame does **not** contain the code: match your order by `to` (the order's `email`) and call `GET /orders/otp` to read it. Up to 5 sockets per account (the oldest is closed). The socket is closed with code 1008 if the key is invalid, revoked or the account is locked. ### GET /me — account ```json { "status": "success", "data": { "id": "…", "email": "you@gmail.com", "balance": 125000, "created_at": "2026-09-08T19:00:00+07:00", "otp_high_priority": false } } ``` ## Errors `message` is the code to match on; follow `retryable`, not the code alone. ### Gmail OTP and any endpoint | HTTP | message | retryable | Meaning — what to do | |---|---|---|---| | 400 | `invalid_request` | false | Missing or malformed parameter (no `service`, no `order_id`, bad JSON body). Fix the call. | | 401 | `unauthorized` | false | API key missing, wrong, rotated or revoked, or the account is locked. Get a new key; do not retry. | | 402 | `insufficient_balance` | false | Balance too low (for the priority tier, `price_high` must be covered). Top up first. | | 403 | `forbidden` | false | Your account type cannot use this endpoint. | | 404 | `not_found` | false | Unknown service code, unknown or refunded order id, or unknown endpoint. Check the spelling. | | 409 | `out_of_stock` | true | No Gmail for this service right now; supply refills continuously. Retry later. | | 409 | `in_progress` | true | An order with this `unique_key` is still being created or may already exist at the supplier. Retry the SAME call with the SAME `unique_key`. | | 409 | `order_cancelled` | true | The order was released before it was charged; nothing was charged. Retry. | | 413 | `payload_too_large` | false | Request body too large. | | 422 | `idempotency_mismatch` | false | This `unique_key` was already used for a different request. Use a new key. | | 503 | `service_unavailable` | true | Could not finish the order right now. Retry the SAME call with the SAME `unique_key`. | | 503 | `shutting_down` | true | Server restarting. Retry shortly (same `unique_key`). | | 503 | `auth_unavailable` | true | The key could not be checked right now. Retry shortly; the key is fine. | | 500 | `internal_server_error` | false | Unexpected server error. If you sent a `unique_key`, retrying with it is safe. | With `in_progress`, `service_unavailable` and `shutting_down`, retrying with the same `unique_key` never charges twice and returns the existing order if one was created. ## Other products (brief) ### Hotmail/Outlook accounts (outright sale) ``` GET /api/v1/stock/products catalogue with stock (no auth); /stock with a key is the same GET /api/v1/orders?api_key=...&service=&quantity=N&unique_key= GET /api/v1/stock/orders?api_key=... your last 100 purchases with their accounts ``` - Catalogue item: `code`, `name`, `category`, `price`, `stock`, `credential_format`, `description`. - `quantity`: 1–100 (larger values are capped at 100). Same `unique_key` rules as Gmail OTP. - Purchase response: `{ "id", "service", "quantity", "amount", "data": ["email|pass|refresh_token|client_id", …] }`. - Errors: 404 `not_found` (unknown product code), 409 `out_of_stock` with `retryable: false` (not enough accounts — wait for a restock), 402 `insufficient_balance`. ### Read a Hotmail/Outlook mailbox from your credentials ``` POST /api/v1/hotmail?api_key=... body: {"accounts": "", "action": "read"|"check"} ``` - Up to 50 accounts per call. `check` tests the login; `read` (default) returns the 5 newest mails with any `otp` / `link` found. Prefer POST: credentials do not belong in a URL (GET with `?accounts=` still works for old tools). - Response: `{ "results": [ { "email", "ok", "live", "error", "otp", "link", "messages": [ { "subject", "from", "folder", "received_at", "otp", "link" } ] } ], "total", "live" }`. - Per-account `error` texts: `invalid format: expected email|pass|refresh_token|client_id`, `token error: `, `could not obtain an access token`, `graph returned HTTP `, `timeout`. - Errors: 400 `empty` (no accounts), 429 `batch_in_progress` (retryable: another batch of yours is running). ### All mail of an order, domain aliases ``` GET /api/v1/mail?api_key=...&order_id=[&limit=20][&body=0] every mail of one order (limit max 100) GET /api/v1/domain/aliases?api_key=...&page=&page_size= Mail Domain aliases, paginated ``` `/mail` returns `{ "email", "count", "messages": [ { "id", "from", "subject", "body", "html", "otp", "link", "pinned", "received_at" } ] }` and 403 `forbidden` if the order is not yours.