Operations

API reference

About this reference

Every operation of the Box REST API: 56 of them, generated from the OpenAPI document (Box API 1.0.0, sha256 e645a3778b2e). New to the API? Start with the guide.

Base URL: https://box.maillogic.io/v1. Every request sends Authorization: Bearer <credential>.

Each operation lists the scopes its credential needs. Operations marked Account owner only are the Console's: only the signed-in owner can call them, never an API key or an OAuth application, whatever scopes it holds:

Rate limits shown with each 429 are Box's defaults. A deployment can configure them, and its own OpenAPI document states the limits it applies, so the wording of some 429 descriptions there can differ from this page.

API overview

Box is MailLogic's API-first mailbox service.

Authentication. Every request carries Authorization: Bearer <credential>: a Box API key (boxk_...), an OAuth access token, or the access token of a MailLogic sign-in. A MailLogic account needs no separate Box signup: its first request sets up its Box customer. API keys belong to one customer, hold explicit scopes (listed per operation as x-required-scopes), and may be restricted to one mailbox. A sign-in holds every scope. API keys are created, listed and revoked with a sign-in (/v1/api-keys); an API key cannot manage API keys.

OAuth. An application acting for a signed-in owner uses OAuth 2.1 (the authorization code flow, with a proof key) instead of a key. Box is one OAuth protected resource, this API and its MCP server alike: the 401 names Box's protected-resource metadata (RFC 9728), which names the authorization server, MailLogic's identity provider. Register a client there (server-side applications as confidential clients, native and command-line applications as public ones), send the owner through the authorization request, and the owner allows the client on Box's consent page, choosing its capabilities (mailboxes:read, messages:read, messages:write; only if the owner chooses them messages:send, messages:delete, messages:destroy, junk:read, junk:write, junk:release, drafts:read and drafts:write; and only if the owner chooses them domains:read, domains:write, mailboxes:write, mailboxes:delete, domains:delete and usage:read) and, optionally, one mailbox (not with those six, which cover the whole account). The resulting access token is the same on this API and on the MCP server, and so is the owner's authorization of the client (/v1/authorizations), which the owner can revoke at any time: the client's next request is then refused. An OAuth client acts with its authorization's access only. It never has an owner's other capabilities: it cannot manage API keys or authorizations, and it cannot use the operations that need a scope outside those (403 auth.insufficient_scope).

Scopes are standing authorization. The scopes an API key was issued, or an OAuth client was granted, are the owner's delegation to it, valid for every call until revoked: they are not evidence that a person approved any one call. No operation takes a confirmation parameter, and nothing in a request raises what its credential holds. An application acting for a person should ask that person before each call that deletes, releases or cannot be undone. The operations that cannot be undone, or that lift a protection, have scopes of their own that no other scope includes: permanently deleting mail (messages:destroy) and releasing mail from Junk (junk:release). A credential issued or granted before those scopes existed does not hold them. Nor does one hold the drafts' scopes (drafts:read, drafts:write) unless they were chosen for it.

Domains. Every mailbox's address is on one of the customer's own domains, such as example.com. A domain is added (createDomain), its ownership is proved with a TXT record (verifyDomainOwnership), its mail records are added to its DNS and checked (checkDomainDns), and then it is active: mailboxes are created on it (createMailbox), and they send and receive mail. A domain's status always says what is left to do, and its mail_readiness whether its mailboxes receive and send now. Domains cover the whole account: their operations need domains:read or domains:write, which an API key or an OAuth client can hold too, and a credential without a mailbox restriction (403 auth.mailbox_restricted). So does creating a mailbox (mailboxes:write). Every mailbox shows the domain it is on (domain_id).

Deleting. A mailbox is deleted with all its mail, permanently (deleteMailbox, mailboxes:delete); a domain only once it has no mailboxes, and deleting a domain never deletes a mailbox (deleteDomain, domains:delete). Each names one resource by its id; there is no bulk deletion and no confirmation parameter: an application deleting for a person should ask that person first. Both scopes cover the whole account, and an OAuth client holds them only if the owner chose them. A deletion that cannot finish now leaves the resource deleting (503); send it again to finish it. Once deleted, a resource is not found (404), like one that never existed: a repeated deletion that answers 404 means the resource is already gone. A deleted resource's id is never used again; its address or name can be used again.

Deleting mail. Deleting a message (deleteMessage) moves it to its mailbox's trash: it keeps its id, can still be read there (folder: trash), and still counts toward the account's stored mail. Only a message in the trash can be permanently deleted (permanentlyDeleteMessage), and emptying the trash (emptyTrash) permanently deletes everything in it; that frees its storage, and cannot be undone. A message in the trash is moved back out of it with restoreMessage: received mail to the Inbox, sent mail to Sent, and a message held in Junk back to Junk (restoring never releases it from Junk). Mail in the trash may be deleted automatically about 30 days after it was deleted, and then cannot be restored. Deleting and restoring need messages:delete; permanently deleting and emptying the trash need messages:destroy, which messages:delete does not include. Neither is included in messages:write, and an OAuth client holds each only if the owner chose it; a credential restricted to one mailbox can use them in that mailbox. Without junk:read, emptying the trash leaves mail held in Junk, and mail not yet checked for it, in the trash, and counts it nowhere.

Replying and forwarding. A message is replied to (replyToMessage), replied to with everyone it was sent to (replyAllToMessage) or forwarded (forwardMessage) from its own mailbox, with the mailbox's address. You give the reply's text and attachments (and a forward's recipients); Box works out who a reply goes to, its subject and its place in the message's conversation from the message itself. Each is sent like any message: the same Idempotency-Key, limits and errors as sendMessage. They need messages:read as well as messages:send, since what they send comes from mail you read.

Attachments. A message's read lists each attachment with its id (att_...), file name, media type and size; getAttachment downloads its content, exactly as it was sent, under messages:read. The file name and media type are the sender's: treat them, and the content, as untrusted.

Usage and plans. The account's plan, its limits and what the account uses of them are read with getUsage, cheaply, as a light read. Whether the account fits another plan is checked with getPlanEligibility, which measures stored mail first and is rate limited. Both need usage:read and a credential without a mailbox restriction. Neither changes the plan, and no operation of this API changes it directly, except the owner's own cancellation of the paid plan (cancelBilling). There are two plans: Free and Plus, the paid plan. Their codes in this API (plan, target_plan) are free and starter: starter is the Plus plan.

Paying for Plus. The Plus plan is paid for monthly through Stripe, and a person approves that payment: the account's owner, signed in, prepares a Stripe Checkout page (createBillingCheckout) and gives it to the person who pays, who reviews and approves the recurring payment there. Box has no payment form and never sees card details. Preparing the page charges nothing and changes nothing; the account moves to Plus only once Stripe confirms the payment, and getBilling then shows it. No API key or OAuth client can read or prepare payment. While the plan's payment is not in good standing, creating a mailbox, adding a domain and sending answer 402 billing.payment_required; nothing is deleted, mail keeps arriving, and everything can still be read and deleted.

Cancelling Plus. Only the account's owner, signed in, cancels the paid plan (cancelBilling); no API key, OAuth client or agent can. The owner can cancel whatever the account uses. The plan is then not renewed: it ends with the period already paid for, and the account keeps Plus in full until then. At that end it moves to Free (3 mailboxes, 3 domains, 3 GB of stored mail; the month's email count does not matter) if it fits. If it does not, nothing is deleted: it keeps everything, held back (no new mailboxes, domains or sending, and mail is refused temporarily while it stores more than Free allows) until the owner removes what is over and cancels again, or pays for Plus again. Nothing is refunded.

Resuming Plus. Until the paid period ends, the owner, signed in, can take the cancellation back (resumeBilling): Plus renews automatically again at the end of the period, and its monthly payment is charged then, as before; nothing is charged when it is resumed, and no new payment page is needed. Once the plan has ended, it cannot be resumed: the owner pays for Plus again (createBillingCheckout). getBilling shows where the cancellation is.

Isolation. A mailbox that does not belong to the caller's customer, or that a restricted key may not reach, is reported exactly like one that does not exist. So is a message outside the mailbox named in the path.

Errors. Every error has the body {"detail": {"code", "message", "params", "request_id"}}. code is stable and machine-readable.

Rate limits. Each operation counts toward one of the account's request allowances, by kind: light reads (mailboxes, domains, usage, events, webhooks), reading mail, searching, downloading attachments, changing messages, emptying the trash, checking domains, and creating or deleting mailboxes, domains and webhooks. Each allows a number of requests at once and refills at a steady rate (see each operation's 429). An allowance is the account's, shared by this API and Box's MCP server and by every credential of the account. Past it, a request is refused with 429 auth.rate_limited, params.limit naming the allowance and params.scope account, and Retry-After: the whole seconds until it fits again. Nothing was done; wait at least that long before retrying, and do not poll in a tight loop. A request refused before it reaches its operation (401, 403, an invalid request) counts toward nothing; one that reaches it counts, whatever its outcome. Sending has allowances of its own instead: each mailbox's sent messages (send.messages, params.scope mailbox) and the account's recipients (send.recipients), charged only for a new message (never for a retry of one already sent or being sent), besides the plan's monthly email count. So do the attachment bytes the account downloads from storage (attachment.bytes): downloading an attachment, and forwarding a message with its attachments. The account owner's operations keep their own limits. Separately, an operation that works on stored mail is refused with 429 auth.rate_limited and empty params (Retry-After: 1) while too many of the account's requests are already in progress.

Writes. Sending a message needs an Idempotency-Key, so that retrying it is always safe, and so do replying to one and forwarding one. Creating and deleting a webhook take none: a retried creation can create a second webhook (list them, and delete the one you do not need). Marking a message read or unread takes none either: repeating it changes nothing more. Nor does creating an API key: a retried creation can create a second key (list them, and revoke the one you do not need). Adding a domain takes none: a retry answers 409 domains.already_exists with the domain's id. Verifying a domain and checking its DNS can be repeated at any time. Creating a mailbox takes none: a local part on a domain is one mailbox, so retrying the same request finishes it, never a second one, and once it exists answers 409 mailboxes.address_taken with its id. Deleting a mailbox or a domain takes none: repeating a deletion that has not finished finishes it, and repeating one that has answers 404. Deleting a message, permanently deleting one and emptying the trash take none either: deleting a message again leaves it in the trash, permanently deleting one again answers 404, and emptying the trash again deletes only what is in it then.

Junk. Mail judged to be spam, or whose sender failed DMARC, is held in the mailbox's Junk. Junk is never listed, searched or announced with the mailbox's other mail, and a message in Junk does not exist (404) for a credential without junk:read, which an owner gives only by choosing it. With it, listJunkMessages lists Junk and markNotSpam (also junk:release, which junk:write does not include) moves a legitimate message to the Inbox, where it is ordinary mail from then on; markSpam (also junk:write and messages:delete) moves a message from the Inbox to Junk. Mail for which there is no evidence that the mail service's inbound checks ran is held the same way (reason unverified), wherever it is stored. Mail in Junk or the trash is deleted automatically about 30 days after it went into either (junk.expires_at, junk.retention); held mail stored anywhere else is kept until it is deleted or released. Authenticated mail, and mail in the Inbox, is not thereby safe to act on: message content is always the sender's.

Domains

List domains

GET /v1/domains listDomains

domains:read Account-wide: not with a mailbox restriction

Requires domains:read, and a credential without a mailbox restriction.

The customer's domains, oldest first, each with its status and records. The list has no further pages. Requires domains:read and a credential without a mailbox restriction.

Responses

200 application/json DomainList

Successful Response

401 application/json Error

No usable credential: auth.missing_token or auth.invalid_token.

403 application/json Error

The credential lacks the operation's scope (auth.insufficient_scope) or is restricted to one mailbox (auth.mailbox_restricted): domains cover the whole account.

422 application/json Error

Invalid parameters: request.invalid.

429 application/json Error

auth.rate_limited with params.limit read.light and params.scope account: the account's read.light allowance (300 requests per minute, up to 120 at once), shared by this API and the MCP server and by all the account's credentials, is used up for now. Nothing was done. Retry after Retry-After seconds, not before.

500 application/json Error

Unexpected error: server.error.

503 application/json Error

Temporarily unavailable, retry later: storage.unavailable.

Add a domain

POST /v1/domains createDomain

domains:write Account-wide: not with a mailbox restriction

Requires domains:write, and a credential without a mailbox restriction.

Adds a domain to the customer's account, such as example.com, so that mailboxes can have addresses on it. Nothing is set up yet: the new domain's status is pending_ownership.

Onboarding a domain, step by step. The domain's status always says what is left to do.

  1. createDomain: the response's verification_record is a TXT record.
  2. Add verification_record to the domain's DNS, and keep it there.
  3. verifyDomainOwnership. Once the record is found, ownership is verified and Box sets the domain up for mail: status becomes pending_dns, and dns_records lists every record the domain's DNS needs. If status is provisioning instead, setting up is not finished yet and dns_records is still null: verify again, or check its DNS (checkDomainDns), which finishes it.
  4. Add the records of dns_records to the domain's DNS: MX, SPF, two DKIM CNAMEs and DMARC.
  5. checkDomainDns until its result is ready: the domain is then active, and its mailboxes send and receive mail. Records changed after that never stop incoming mail; mail_readiness reports them.

DNS changes can take a while to be seen everywhere: repeat a verification or a check until it finds them.

One account per domain. A domain is in an account once: adding it again is 409 domains.already_exists, with its id in params.domain_id. Other accounts may add the same domain, but only one of them can verify its ownership. Box's own domains cannot be added (422 domains.reserved).

Eligibility. A domain associated with known abuse cannot be added or verified (422 domains.reputation_listed).

Limits. The plan limits how many domains an account has (403 entitlements.domain_limit). Every domain in the account counts, whatever its status, until it is deleted; a subdomain is a domain of its own. A domain being deleted counts until its deletion has finished.

Requires domains:write and a credential without a mailbox restriction.

Request body application/json required

NewDomain

FieldTypeDescription
name requiredstring

The domain to add, such as example.com or mail.example.com: a domain name of at most 253 characters, internationalized names in their xn-- form. Case and a trailing dot are ignored. An address, a URL or a wildcard is refused (domains.invalid).

Responses

201 application/json Domain

Successful Response

401 application/json Error

No usable credential: auth.missing_token or auth.invalid_token.

402 application/json Error

billing.payment_required: the account's plan is paid for through Stripe and its payment is not in good standing (params.status: past_due, unpaid, paused or canceled), so new mailboxes, domains and outgoing mail are on hold. Nothing was created or sent. Existing mail is kept, mail keeps arriving, and mailboxes and domains can still be read and deleted. The account's owner resolves it.

403 application/json Error

The credential lacks the operation's scope (auth.insufficient_scope) or is restricted to one mailbox (auth.mailbox_restricted): domains cover the whole account. Or the plan's domain limit is reached (entitlements.domain_limit).

409 application/json Error

domains.already_exists: the domain is already in this account; params.domain_id is its id.

413 application/json Error

request.too_large: the request body is too large.

422 application/json Error

Invalid body (request.invalid), a name that is not a domain name (domains.invalid, with params.fields), one of Box's own domains (domains.reserved), or a domain associated with known abuse (domains.reputation_listed).

429 application/json Error

auth.rate_limited with params.limit account.change and params.scope account: the account's account.change allowance (30 requests per hour, up to 10 at once), shared by this API and the MCP server and by all the account's credentials, is used up for now. Nothing was done. Retry after Retry-After seconds, not before.

500 application/json Error

Unexpected error: server.error.

503 application/json Error

Temporarily unavailable, retry later: storage.unavailable.

Get a domain

GET /v1/domains/{domain_id} getDomain

domains:read Account-wide: not with a mailbox restriction

Requires domains:read, and a credential without a mailbox restriction.

One domain: what is left to do before it carries mail (status), the record that proves its ownership (verification_record) and, once it is set up for mail, every record its DNS needs (dns_records). Another account's domain is 404 domains.not_found, like one that does not exist. Requires domains:read and a credential without a mailbox restriction.

Parameters

NameInTypeDescription
domain_id requiredpathstring

Responses

200 application/json Domain

Successful Response

401 application/json Error

No usable credential: auth.missing_token or auth.invalid_token.

403 application/json Error

The credential lacks the operation's scope (auth.insufficient_scope) or is restricted to one mailbox (auth.mailbox_restricted): domains cover the whole account.

404 application/json Error

domains.not_found: no such domain in this account. Another account's domain is reported exactly the same way.

422 application/json Error

Invalid parameters: request.invalid.

429 application/json Error

auth.rate_limited with params.limit read.light and params.scope account: the account's read.light allowance (300 requests per minute, up to 120 at once), shared by this API and the MCP server and by all the account's credentials, is used up for now. Nothing was done. Retry after Retry-After seconds, not before.

500 application/json Error

Unexpected error: server.error.

503 application/json Error

Temporarily unavailable, retry later: storage.unavailable.

Delete a domain that has no mailboxes

DELETE /v1/domains/{domain_id} deleteDomain

domains:delete Account-wide: not with a mailbox restriction

Requires domains:delete, and a credential without a mailbox restriction.

Deletes one domain, named by its id. A domain with mailboxes is not deleted: 409 domains.has_mailboxes names them (params.count, and params.mailboxes, at most 50, each with its id, address and status). Deleting a domain never deletes a mailbox; each is deleted on its own, with all its mail (deleteMailbox), or kept.

A domain whose ownership was never verified is deleted at once. A verified one first stops being usable (status: deleting): no mailbox is created on it and it is not verified or checked again, and it stays reserved for this account while Box removes what it set up for the domain's mail (its DKIM key, its mail routing). Then it is gone, and its name can be added again, by this account or another. Its records in the domain's own DNS are no longer used and can be removed.

Retrying. A deletion that could not finish (503 domains.deletion_unavailable) leaves the domain deleting; sending the same deletion again finishes it. Once deleted, the domain is not found: a repeated deletion answering 404 domains.not_found means it is already gone.

Requires domains:delete and a credential without a mailbox restriction: domains:write does not allow it.

Parameters

NameInTypeDescription
domain_id requiredpathstring

Responses

204

Successful Response

401 application/json Error

No usable credential: auth.missing_token or auth.invalid_token.

403 application/json Error

The credential lacks the operation's scope (auth.insufficient_scope) or is restricted to one mailbox (auth.mailbox_restricted): domains cover the whole account.

404 application/json Error

domains.not_found: no such domain in this account, or it is already deleted. Another account's domain is reported exactly the same way.

409 application/json Error

domains.has_mailboxes: the domain has mailboxes (params.count, params.mailboxes); nothing was changed. domains.deletion_in_progress: another request is deleting the domain or setting it up for mail; retry after Retry-After seconds (5).

422 application/json Error

Invalid parameters: request.invalid.

429 application/json Error

auth.rate_limited with params.limit account.change and params.scope account: the account's account.change allowance (30 requests per hour, up to 10 at once), shared by this API and the MCP server and by all the account's credentials, is used up for now. Nothing was done. Retry after Retry-After seconds, not before. auth.rate_limited with empty params: too many of the account's requests are in progress at once. Nothing was done. Retry after Retry-After seconds (1).

500 application/json Error

Unexpected error: server.error.

503 application/json Error

domains.deletion_unavailable: the domain is deleting but what Box set up for it could not all be removed now; send the same deletion again after Retry-After seconds (30). storage.unavailable.

Check a domain's mail records

POST /v1/domains/{domain_id}/check-dns checkDomainDns

domains:write Account-wide: not with a mailbox restriction

Requires domains:write, and a credential without a mailbox restriction.

Checks the domain's mail records in its DNS: MX, SPF, both DKIM records and DMARC, as its dns_records lists them.

  • result: ready: every record is as required. The domain is now active: its mailboxes send and receive mail.
  • result: not_ready: records gives each record's status (ok, missing, mismatch, multiple or invalid) and what the DNS holds there (found). Fix the records and check again. On an active domain this never stops incoming mail and never undoes active: the domain's mail_readiness reports it. Wrong sending records (SPF, the DKIM record Box signs with) make mail_readiness.outbound degraded, and invalid once checks over at least 30 minutes, confirmed by a second, independent DNS lookup, find them wrong: its mailboxes' sends are then refused (mail_readiness.sending) until a check finds them as required, likewise confirmed. MX, DMARC and the other DKIM record are reported as warnings and never stop mail.
  • A DNS that does not answer (503 domains.dns_unavailable) changes nothing but mail_readiness.last_attempt.
  • SPF: the domain's one SPF record must contain required_mechanism before any all term (with a -, ~ or ? qualifier it does not count). A domain without an SPF record adds the recommended value. DMARC: the domain's own valid DMARC record is kept; a domain without one adds the recommended value.
  • Ownership must be verified first (409 domains.ownership_not_verified). A domain whose setting up for mail is not finished (provisioning) is set up first.

Checking records its result, so it needs domains:write. These errors change nothing; retry after Retry-After seconds:

ErrorRetry-After
503 domains.dns_unavailable: the DNS gave no answer5
409 domains.provisioning_in_progress: the domain is being set up for mail5
503 domains.provisioning_unavailable: the domain cannot be set up for mail right now30
409 domains.check_in_progress: the domain changed during the check1

Another account's domain is 404 domains.not_found, like one that does not exist, and nothing is looked up for it. Requires domains:write and a credential without a mailbox restriction.

Parameters

NameInTypeDescription
domain_id requiredpathstring

Responses

200 application/json DomainDnsCheck

Successful Response

401 application/json Error

No usable credential: auth.missing_token or auth.invalid_token.

403 application/json Error

The credential lacks the operation's scope (auth.insufficient_scope) or is restricted to one mailbox (auth.mailbox_restricted): domains cover the whole account.

404 application/json Error

domains.not_found: no such domain in this account. Another account's domain is reported exactly the same way.

409 application/json Error

domains.ownership_not_verified: verify the domain's ownership first. domains.provisioning_in_progress: the domain is being set up for mail; retry after Retry-After seconds (5). domains.check_in_progress: the domain changed during the check; retry after Retry-After seconds (1). domains.deleting: the domain is being deleted.

422 application/json Error

Invalid parameters: request.invalid.

429 application/json Error

auth.rate_limited with params.limit domains.check and params.scope account: the account's domains.check allowance (30 requests per hour, up to 10 at once), shared by this API and the MCP server and by all the account's credentials, is used up for now. Nothing was done. Retry after Retry-After seconds, not before. auth.rate_limited with empty params: too many of the account's requests are in progress at once. Nothing was done. Retry after Retry-After seconds (1).

500 application/json Error

Unexpected error: server.error.

503 application/json Error

Nothing was changed; retry after Retry-After seconds: domains.provisioning_unavailable (30), the domain cannot be set up for mail right now; domains.dns_unavailable (5), the DNS gave no answer. storage.unavailable.

Find out whether the domain's DNS provider can add its records

GET /v1/domains/{domain_id}/domain-connect getDomainConnect

domains:read Account-wide: not with a mailbox restriction

Requires domains:read, and a credential without a mailbox restriction.

Whether the domain's DNS provider can add the domain's records itself, once the person approves the changes there, and exactly what each record would do to the domain's DNS as it is now. It changes nothing.

  • Stages. ownership while ownership is not verified: the verification record only. mail once the domain is pending_dns: its mail records. A domain that is provisioning, active or deleting has none (reason).
  • available: false: reason says why. The records are then added by hand, as verification_record and dns_records show them.
  • changes: every record of the stage, and its action on the domain's DNS: add, replace, merge (SPF), keep (the domain's own DMARC record stays), unchanged.
  • warnings: what the person must know before approving. Above all mx_replaces_existing: the domain's incoming mail moves to Box, and stops arriving where it arrives now; addresses without a mailbox in Box are refused until they have one.
  • blockers: a change that would destroy or make ambiguous what is there (a website's CNAME, an SPF record Box cannot add its term to safely, another service's DKIM record): nothing is offered.
  • The DNS or the DNS provider not answering is 503 domain_connect.discovery_unavailable: nothing is concluded, and an unanswered lookup is never taken to mean a record is absent.

Only DNS providers that alone serve the domain's DNS are used, through their own fixed addresses. Another account's domain is 404 domains.not_found, and nothing is looked up for it. Requires domains:read and a credential without a mailbox restriction.

Parameters

NameInTypeDescription
domain_id requiredpathstring

Responses

200 application/json DomainConnect

Successful Response

401 application/json Error

No usable credential: auth.missing_token or auth.invalid_token.

403 application/json Error

The credential lacks the operation's scope (auth.insufficient_scope) or is restricted to one mailbox (auth.mailbox_restricted): domains cover the whole account.

404 application/json Error

domains.not_found: no such domain in this account. Another account's domain is reported exactly the same way.

422 application/json Error

Invalid parameters: request.invalid.

429 application/json Error

auth.rate_limited with params.limit domains.connect and params.scope account: the account's domains.connect allowance (20 requests per hour, up to 5 at once), shared by this API and the MCP server and by all the account's credentials, is used up for now. Nothing was done. Retry after Retry-After seconds, not before.

500 application/json Error

Unexpected error: server.error.

503 application/json Error

domain_connect.discovery_unavailable: the domain's DNS or its DNS provider did not answer; nothing was concluded. Retry after Retry-After seconds. storage.unavailable.

Prepare the DNS provider's page where the person approves the domain's records

POST /v1/domains/{domain_id}/domain-connect/authorizations createDomainConnectAuthorization

domains:write Account-wide: not with a mailbox restriction

Requires domains:write, and a credential without a mailbox restriction.

Prepares the page of the domain's DNS provider where the person approves the current stage's changes (authorization_url). Preparing it changes nothing, in Box or in the domain's DNS: only the person, signed in at the DNS provider, can make the changes. Open the address in the person's own browser.

  1. getDomainConnect, and show the person changes and warnings.
  2. If requires_mx_acknowledgment is true, the domain's MX records are replaced and all its incoming mail moves to Box: ask the person, and set accept_mx_change only once they agree. Without it the request is 409 domain_connect.mx_acknowledgment_required (params.current).
  3. This request, with the stage read in step 1.
  4. The person approves the changes at the DNS provider, which then sends them back to Console.
  5. verifyDomainOwnership (next_check: ownership) or checkDomainDns (next_check: dns): only these establish what changed. A domain is never verified or ready because the person came back from the DNS provider. DNS changes can take a while to be seen.

A domain takes two approvals: its verification record first, then, once ownership is verified and the domain is set up for mail, its mail records.

  • The address carries everything the page does, and the DNS provider refuses it if any of it is changed. It does not expire and can be opened again (approving the same changes again adds nothing), and revoking the credential that prepared it does not withdraw it: prepare a new one when the domain changes.
  • When the link reaches the person through an app that may rewrite the links it shows (an AI assistant's chat, for example), give them console_url instead: MailLogic Console's page for the domain's automatic setup, where, signed in, they see the changes and go on to the DNS provider. It names only the domain and the stage, and works with parameters an app adds to it.
  • 409 domain_connect.unavailable: the stage is not available (params.reason, as getDomainConnect reports it, and params.blockers for blocked). 409 domain_connect.stage_changed: the domain is at another stage now (params.stage). 503 domain_connect.discovery_unavailable: retry after Retry-After seconds.
  • Each preparation is recorded in the account's audit log, never with its address.

Requires domains:write and a credential without a mailbox restriction.

Parameters

NameInTypeDescription
domain_id requiredpathstring

Request body application/json required

NewDomainConnectAuthorization

FieldTypeDescription
accept_mx_changeboolean

The person agreed that the domain's MX records are replaced and its incoming mail moves to Box. Needed when requires_mx_acknowledgment is true; set it only with the person's agreement.

default false

stagestring: ownership, mail or null

The stage the caller expects (stage of the domain's availability); refused (domain_connect.stage_changed) if the domain has moved on. Omit it for the current stage.

default null

Responses

200 application/json DomainConnectAuthorization

Successful Response

401 application/json Error

No usable credential: auth.missing_token or auth.invalid_token.

403 application/json Error

The credential lacks the operation's scope (auth.insufficient_scope) or is restricted to one mailbox (auth.mailbox_restricted): domains cover the whole account.

404 application/json Error

domains.not_found: no such domain in this account. Another account's domain is reported exactly the same way.

409 application/json Error

domain_connect.unavailable: the stage is not available (params.reason). domain_connect.stage_changed: the domain is at another stage now (params.stage). domain_connect.mx_acknowledgment_required: the changes replace the domain's MX records and accept_mx_change is not set.

413 application/json Error

request.too_large: the request body is too large.

422 application/json Error

Invalid parameters: request.invalid.

429 application/json Error

auth.rate_limited with params.limit domains.connect and params.scope account: the account's domains.connect allowance (20 requests per hour, up to 5 at once), shared by this API and the MCP server and by all the account's credentials, is used up for now. Nothing was done. Retry after Retry-After seconds, not before.

500 application/json Error

Unexpected error: server.error.

503 application/json Error

domain_connect.discovery_unavailable: the domain's DNS or its DNS provider did not answer; nothing was concluded. Retry after Retry-After seconds. storage.unavailable.

Verify a domain's ownership

POST /v1/domains/{domain_id}/verify verifyDomainOwnership

domains:write Account-wide: not with a mailbox restriction

Requires domains:write, and a credential without a mailbox restriction.

Looks for the domain's verification record (verification_record) in its DNS, and verifies the domain's ownership if it is there.

  • result says what was found: verified, record_missing or value_mismatch. Not finding the record is an answer, not an error: add it to the domain's DNS and verify again.
  • Once ownership is verified, verifying again answers already_verified and looks nothing up.
  • Setting up for mail. Once ownership is verified, Box sets the domain up for mail, and domain.status becomes pending_dns, with every record to add in domain.dns_records. If that cannot be finished right away, ownership stays verified all the same: domain.status is provisioning, and the next verification or DNS check (checkDomainDns) finishes it.
  • Only one account can hold verified ownership of a domain: when another account holds it, the answer is 409 domains.ownership_already_verified.
  • A domain associated with known abuse is not verified (422 domains.reputation_listed), and nothing is changed.
  • 503 domains.dns_unavailable: the DNS gave no answer, and nothing was changed. 503 domains.reputation_unavailable: the domain's eligibility could not be checked, and nothing was changed. 409 domains.check_in_progress: the domain changed during the check. Retry any of them after Retry-After seconds.

Another account's domain is 404 domains.not_found, like one that does not exist, and nothing is looked up for it. Requires domains:write and a credential without a mailbox restriction.

Parameters

NameInTypeDescription
domain_id requiredpathstring

Responses

200 application/json DomainOwnershipCheck

Successful Response

401 application/json Error

No usable credential: auth.missing_token or auth.invalid_token.

403 application/json Error

The credential lacks the operation's scope (auth.insufficient_scope) or is restricted to one mailbox (auth.mailbox_restricted): domains cover the whole account.

404 application/json Error

domains.not_found: no such domain in this account. Another account's domain is reported exactly the same way.

409 application/json Error

domains.ownership_already_verified: another account holds verified ownership of this domain. domains.check_in_progress: the domain changed during the check; retry after Retry-After seconds. domains.deleting: the domain is being deleted.

422 application/json Error

domains.reputation_listed: the domain is associated with known abuse and is not verified. request.invalid.

429 application/json Error

auth.rate_limited with params.limit domains.check and params.scope account: the account's domains.check allowance (30 requests per hour, up to 10 at once), shared by this API and the MCP server and by all the account's credentials, is used up for now. Nothing was done. Retry after Retry-After seconds, not before.

500 application/json Error

Unexpected error: server.error.

503 application/json Error

Nothing was changed; retry after Retry-After seconds: domains.dns_unavailable (5), the DNS gave no answer; domains.reputation_unavailable (30), the domain's eligibility could not be checked. storage.unavailable.

Mailboxes

List mailboxes

GET /v1/mailboxes listMailboxes

mailboxes:read

Requires mailboxes:read.

The customer's mailboxes, or for a restricted key the ones it may reach.

Responses

200 application/json MailboxList

Successful Response

401 application/json Error

No usable credential: auth.missing_token or auth.invalid_token.

403 application/json Error

The credential lacks the operation's scope: auth.insufficient_scope.

422 application/json Error

Invalid parameters: request.invalid.

429 application/json Error

auth.rate_limited with params.limit read.light and params.scope account: the account's read.light allowance (300 requests per minute, up to 120 at once), shared by this API and the MCP server and by all the account's credentials, is used up for now. Nothing was done. Retry after Retry-After seconds, not before.

500 application/json Error

Unexpected error: server.error.

503 application/json Error

Temporarily unavailable, retry later: storage.unavailable.

Create a mailbox

POST /v1/mailboxes createMailbox

mailboxes:write Account-wide: not with a mailbox restriction

Requires mailboxes:write, and a credential without a mailbox restriction.

Creates a mailbox on one of the account's domains, and returns it. It sends and receives mail at once, and the mail it receives is on the event feed (message.received) like any other mailbox's.

The address is local_part at the name of the domain domain_id. The domain is named by its id, never by a name, so a mailbox is only ever created on a domain of the account. It must be active (409 domains.inactive otherwise: its status says what is left to do). Another account's domain is 404 domains.not_found, like one that does not exist, and nothing is created for it.

The local part is 1 to 64 lower-case letters, digits, ., _ and -, starting and ending with a letter or digit, without ..; upper case is accepted and stored lower-cased. Names reserved for the mail system, such as postmaster, abuse or noreply, are refused like any other invalid local part (422 mailboxes.invalid_address). The same local part can be used on each of the account's domains.

The owner's sign-in address is never a mailbox: an address equal to the email address the account owner signs in to Box with (as the owner's latest sign-in named it, compared without regard to case) is 422 mailboxes.sign_in_address. Every other address on the domain can be used. Until Box knows that address (the owner has not signed in to Box since it started keeping it), no mailbox is created through any credential: 409 mailboxes.sign_in_email_unknown; the owner signs in once, and the same request can then be retried.

Retrying is safe. A local part on a domain is one mailbox, however often it is asked for:

  • after a failure (503 mailboxes.provisioning_unavailable, 503 storage.unavailable, 500 server.error) or no response at all, send the same request again: it finishes the same mailbox, never a second one;
  • while another request is creating it, the answer is 409 mailboxes.provisioning_in_progress;
  • once it exists, the answer is 409 mailboxes.address_taken, with its id in params.mailbox_id. Without params.mailbox_id, the address is not available to this account.
ErrorRetry-After
409 mailboxes.provisioning_in_progress: another request is creating this mailbox5
503 mailboxes.provisioning_unavailable: the mailbox could not be created now30

Limits. The plan limits how many mailboxes an account has (403 entitlements.mailbox_limit).

Requires mailboxes:write and a credential without a mailbox restriction: creating a mailbox covers the whole account, so a credential restricted to one mailbox never creates another (403 auth.mailbox_restricted).

Request body application/json required

NewMailbox

FieldTypeDescription
domain_id requiredstring

The domain the mailbox is on (dom_...), one of the account's domains. It must be active.

min length 1 · max length 64

local_part requiredstring

The part of the address before the @: 1 to 64 lower-case letters, digits, ., _ and -, starting and ending with a letter or digit, without ... Upper case is accepted and stored lower-cased. Names reserved for the mail system, such as postmaster or abuse, are refused.

Responses

201 application/json Mailbox

Successful Response

401 application/json Error

No usable credential: auth.missing_token or auth.invalid_token.

402 application/json Error

billing.payment_required: the account's plan is paid for through Stripe and its payment is not in good standing (params.status: past_due, unpaid, paused or canceled), so new mailboxes, domains and outgoing mail are on hold. Nothing was created or sent. Existing mail is kept, mail keeps arriving, and mailboxes and domains can still be read and deleted. The account's owner resolves it.

403 application/json Error

The credential lacks mailboxes:write (auth.insufficient_scope) or is restricted to one mailbox (auth.mailbox_restricted): creating a mailbox covers the whole account. Or the plan's mailbox limit is reached (entitlements.mailbox_limit).

404 application/json Error

domains.not_found: domain_id names no domain of this account. Another account's domain is reported exactly the same way.

409 application/json Error

domains.inactive: the domain is not active yet. domains.deleting: the domain is being deleted. mailboxes.address_taken: the address is in use (params.mailbox_id when it is this account's mailbox). mailboxes.provisioning_in_progress: another request is creating this mailbox; retry after Retry-After seconds. mailboxes.sign_in_email_unknown: Box does not know the account owner's sign-in email address yet; the owner signs in to Box once, then retry.

413 application/json Error

request.too_large: the request body is too large.

422 application/json Error

Invalid body (request.invalid, with params.fields), or a local part that cannot be used (mailboxes.invalid_address), or the address is the account owner's sign-in email address (mailboxes.sign_in_address).

429 application/json Error

auth.rate_limited with params.limit account.change and params.scope account: the account's account.change allowance (30 requests per hour, up to 10 at once), shared by this API and the MCP server and by all the account's credentials, is used up for now. Nothing was done. Retry after Retry-After seconds, not before. auth.rate_limited with empty params: too many of the account's requests are in progress at once. Nothing was done. Retry after Retry-After seconds (1).

500 application/json Error

Unexpected error: server.error.

503 application/json Error

mailboxes.provisioning_unavailable: the mailbox could not be created now; send the same request again after Retry-After seconds. storage.unavailable.

Get a mailbox

GET /v1/mailboxes/{mailbox_id} getMailbox

mailboxes:read

Requires mailboxes:read.

One mailbox, with its usage. 404 mailboxes.not_found if the credential cannot reach it.

Parameters

NameInTypeDescription
mailbox_id requiredpathstring

Responses

200 application/json MailboxDetail

Successful Response

401 application/json Error

No usable credential: auth.missing_token or auth.invalid_token.

403 application/json Error

The credential lacks the operation's scope: auth.insufficient_scope.

404 application/json Error

mailboxes.not_found: no such mailbox, or none this credential can reach.

422 application/json Error

Invalid parameters: request.invalid.

429 application/json Error

auth.rate_limited with params.limit read.light and params.scope account: the account's read.light allowance (300 requests per minute, up to 120 at once), shared by this API and the MCP server and by all the account's credentials, is used up for now. Nothing was done. Retry after Retry-After seconds, not before.

500 application/json Error

Unexpected error: server.error.

503 application/json Error

Temporarily unavailable, retry later: storage.unavailable.

Set a mailbox's display name

PATCH /v1/mailboxes/{mailbox_id} updateMailbox

Account owner only

Only the account owner, signed in with MailLogic (the Console or the webmail), can call this. API keys and OAuth application tokens are refused with 403 auth.sign_in_required, whatever scopes they hold.

Sets or clears the mailbox's display name, the name its mail is sent with: display_name <address> in From (Customer Support <support@example.com>), on every message, reply, reply to all, forward and draft the mailbox sends from then on, through /v1, MCP or the webmail alike. Null, or only white space, clears it: mail is then sent from the bare address. The body holds display_name and nothing else.

A display name is presentation only: it never changes the mailbox's address, who may send from it or what may be sent. No request names a From: Box writes it, from the mailbox, as each send is authorized. A send already authorized goes with the name it had then, whatever it is changed to meanwhile; messages sent earlier keep the name they were sent with (from_name on the mailbox's sent mail).

The name is stored normalized (Unicode NFC, trimmed, runs of spaces made one) and answered as stored. At most 64 characters and 200 bytes; any language, accents, emoji, quotes and punctuation. Refused with 422 mailboxes.invalid_display_name and params.reason: too_long; characters (a control character or line break, a bidirectional override or isolate); address_like (@, < or >, also as full-width forms: a name must not pass for an address); encoded_word (=?, which mail programs would decode into something else).

Only the account owner changes it, signed in with MailLogic (the console). An API key or an OAuth client, and so no agent over /v1 or MCP, is refused with 403 auth.sign_in_required, whatever it holds: mailboxes:write creates mailboxes and never names how their mail is sent. Every credential reads the name (listMailboxes, getMailbox). A suspended mailbox can be named; one being deleted is not found.

Parameters

NameInTypeDescription
mailbox_id requiredpathstring

Request body application/json required

MailboxUpdate

FieldTypeDescription
display_name requiredstring or null

The name the mailbox's mail is sent with, or null (or only white space) for none. Stored normalized (Unicode NFC, trimmed, runs of spaces made one). At most 64 characters and 200 bytes in UTF-8; any script, accents, emoji, quotes and punctuation are fine. Refused: control characters and line breaks, bidirectional overrides and isolates, anything that reads as an email address (@, <, >) and the encoded-word marker =?, which mail programs would show as something else.

Responses

200 application/json Mailbox

Successful Response

401 application/json Error

No usable credential: auth.missing_token or auth.invalid_token.

403 application/json Error

auth.sign_in_required: only the account owner's MailLogic sign-in changes a mailbox; an API key or an OAuth client is refused whatever it holds, mailboxes:write included.

404 application/json Error

mailboxes.not_found: no such mailbox in this account, or it is being deleted. Another account's mailbox is reported exactly the same way.

413 application/json Error

request.too_large: the request body is too large.

422 application/json Error

Invalid body (request.invalid, with params.fields: only display_name, a string or null), or a name that cannot be used (mailboxes.invalid_display_name, with params.reason: too_long, characters, address_like or encoded_word).

429 application/json Error

auth.rate_limited with params.limit account.change and params.scope account: the account's account.change allowance (30 requests per hour, up to 10 at once), shared by this API and the MCP server and by all the account's credentials, is used up for now. Nothing was done. Retry after Retry-After seconds, not before.

500 application/json Error

Unexpected error: server.error.

503 application/json Error

storage.unavailable.

Delete a mailbox and all its mail

DELETE /v1/mailboxes/{mailbox_id} deleteMailbox

mailboxes:delete Account-wide: not with a mailbox restriction

Requires mailboxes:delete, and a credential without a mailbox restriction.

Deletes one mailbox, named by its id, and all of its stored mail, permanently: it cannot be recovered. From the start of the deletion the mailbox receives nothing (mail to it is refused), sends nothing and cannot be read. Messages it sent that were already accepted for delivery may still be delivered. API keys and OAuth authorizations restricted to it are revoked. Its events stay on the event feed, with the sender, subject and recipient they named removed (data.redacted: true).

Its address can then be used for a new mailbox (createMailbox), which gets a new id; the deleted mailbox's id is never used again.

Retrying. A deletion that could not finish (503 mailboxes.deletion_unavailable) leaves the mailbox deleting: disabled, listed with that status, and finished by sending the same deletion again. Once deleted, the mailbox is not found: a repeated deletion answering 404 mailboxes.not_found means it is already gone.

A reservation left by a mailbox creation that never finished (named with the status provisioning by deleteDomain's refusal) can be deleted the same way.

Requires mailboxes:delete and a credential without a mailbox restriction: mailboxes:write (creating) does not allow it.

Parameters

NameInTypeDescription
mailbox_id requiredpathstring

Responses

204

Successful Response

401 application/json Error

No usable credential: auth.missing_token or auth.invalid_token.

403 application/json Error

The credential lacks mailboxes:delete (auth.insufficient_scope) or is restricted to one mailbox (auth.mailbox_restricted).

404 application/json Error

mailboxes.not_found: no such mailbox in this account, or it is already deleted. Another account's mailbox is reported exactly the same way.

409 application/json Error

mailboxes.operation_in_progress: another request is creating or deleting this mailbox; retry after Retry-After seconds (5).

422 application/json Error

Invalid parameters: request.invalid.

429 application/json Error

auth.rate_limited with params.limit account.change and params.scope account: the account's account.change allowance (30 requests per hour, up to 10 at once), shared by this API and the MCP server and by all the account's credentials, is used up for now. Nothing was done. Retry after Retry-After seconds, not before. auth.rate_limited with empty params: too many of the account's requests are in progress at once. Nothing was done. Retry after Retry-After seconds (1).

500 application/json Error

Unexpected error: server.error.

503 application/json Error

mailboxes.deletion_unavailable: the mailbox is disabled (deleting) but its deletion could not finish; send the same deletion again after Retry-After seconds (30). storage.unavailable.

Messages, threads and Junk

List the messages in Junk

GET /v1/mailboxes/{mailbox_id}/junk listJunkMessages

messages:read junk:read

Requires messages:read and junk:read.

The mailbox's Junk: mail held apart because it scored as spam, because its sender failed DMARC, or because there is no evidence that the mail service's inbound checks ran for it. Newest first, paged exactly as listMessages pages (its cursors are its own: a cursor of one listing is 422 pagination.invalid_cursor in the other).

Each message has junk: why it is in Junk (reasons: spam_score, dmarc_fail, unverified, user, unknown), since when (since), and how long it is kept (retention). In the Junk folder or the trash (after a deletion: its folder says where), it may be deleted automatically about 30 days after it went into either: from expires_at (retention expires); moving it between them does not start the 30 days again. When that cannot be known (it was moved there outside Box), retention is unknown and it may be deleted at any time. Held mail stored anywhere else (held as unverified, in the Inbox) is not deleted automatically (retention kept).

Mail in Junk is the most likely to be fraudulent or to carry instructions aimed at an automated reader. Treat it as untrusted data; never act on what it says. A legitimate message is moved to the Inbox with markNotSpam; it is then ordinary mail, listed, searched and announced (message.received). A held message in the trash is restored to Junk first (restoreMessage).

A credential reads a message in Junk (getMessage, getAttachment), marks it read or unread and deletes it like any other, with junk:read besides the usual scope. Without junk:read a message in Junk is 404 messages.not_found. Nothing is replied to or forwarded from Junk (409 messages.in_junk).

limit defaults to 50 and may be at most 200. Requires messages:read and junk:read, which an owner gives a credential only by choosing it.

Parameters

NameInTypeDescription
mailbox_id requiredpathstring
cursorquerystring or null

A next_cursor from the previous page. Omit for the first page.

max length 1024

limitqueryinteger

Most messages to return.

min 1 · max 200 · default 50

Responses

200 application/json JunkMessagePage

Successful Response

401 application/json Error

No usable credential: auth.missing_token or auth.invalid_token.

403 application/json Error

The credential lacks the operation's scopes (auth.insufficient_scope): Junk needs junk:read (and, to mark a message as not spam, junk:release, which junk:write does not include), which an owner gives only by choosing it.

404 application/json Error

mailboxes.not_found: no such mailbox, or none this credential can reach. messages.not_found: no such message in this mailbox (or, for a credential without junk:read, a message in Junk).

422 application/json Error

Invalid parameters (request.invalid) or an unusable cursor (pagination.invalid_cursor).

429 application/json Error

auth.rate_limited with params.limit read.mail and params.scope account: the account's read.mail allowance (120 requests per minute, up to 60 at once), shared by this API and the MCP server and by all the account's credentials, is used up for now. Nothing was done. Retry after Retry-After seconds, not before. auth.rate_limited with empty params: too many of the account's requests are in progress at once. Nothing was done. Retry after Retry-After seconds (1).

500 application/json Error

Unexpected error: server.error.

503 application/json Error

Temporarily unavailable, retry later: storage.unavailable.

List messages

GET /v1/mailboxes/{mailbox_id}/messages listMessages

messages:read

Requires messages:read.

The mailbox's messages, newest first: by received_at, then by id.

  • Omit cursor for the first page. Pass each response's next_cursor as cursor for the next page; it is null on the last page.
  • Paging through a listing returns the messages the mailbox held when the first page was requested, each once. Mail that arrives later is in the next listing (GET /v1/events tells you when mail arrives); messages permanently deleted in the meantime are left out.
  • A page can hold fewer than limit messages, even when next_cursor is not null.
  • Cursors are opaque and belong to one mailbox. Do not build or modify them. An unusable cursor fails with 422 pagination.invalid_cursor.

Mail is listed once Box has recorded its arrival, as its message.received event is. A message's id is the message_id of its events.

Mail in Junk is never listed here, whatever the credential: it is listed only by listJunkMessages.

One folder. With folder (inbox, sent, drafts, trash, archive or other; never junk), the listing is the messages whose folder is that folder now: where each message is when the page is read, as every read reports it, so a message moved since an earlier page is listed where it is now. Paging is the same, and a folder's cursors are its own (a cursor of one folder, or of the whole listing, is 422 pagination.invalid_cursor in another). Box looks through at most 1,000 of the mailbox's messages for one page, so a page of a folder that holds few of them can be short, or empty, with a next_cursor: follow it.

limit defaults to 50 and may be at most 200. Requires messages:read.

Parameters

NameInTypeDescription
mailbox_id requiredpathstring
cursorquerystring or null

A next_cursor from the previous page. Omit for the first page.

max length 1024

limitqueryinteger

Most messages to return.

min 1 · max 200 · default 50

folderquerystring: inbox, sent, drafts, trash, archive, other or null

Only the messages in this folder now. Omit for every folder (but Junk).

Responses

200 application/json MessagePage

Successful Response

401 application/json Error

No usable credential: auth.missing_token or auth.invalid_token.

403 application/json Error

The credential lacks the operation's scope: auth.insufficient_scope.

404 application/json Error

mailboxes.not_found: no such mailbox, or none this credential can reach. messages.not_found: no such message in this mailbox (or, for a credential without junk:read, a message in Junk).

422 application/json Error

Invalid parameters (request.invalid) or an unusable cursor (pagination.invalid_cursor).

429 application/json Error

auth.rate_limited with params.limit read.mail and params.scope account: the account's read.mail allowance (120 requests per minute, up to 60 at once), shared by this API and the MCP server and by all the account's credentials, is used up for now. Nothing was done. Retry after Retry-After seconds, not before. auth.rate_limited with empty params: too many of the account's requests are in progress at once. Nothing was done. Retry after Retry-After seconds (1).

500 application/json Error

Unexpected error: server.error.

503 application/json Error

Temporarily unavailable, retry later: storage.unavailable.

Send a message

POST /v1/mailboxes/{mailbox_id}/messages sendMessage

messages:send Idempotency-Key required

Requires messages:send.

Sends a message from the mailbox.

202 means Box has accepted the message for delivery (status: submitted). The returned id names the sent message: a retry returns it, and delivery events carry it. The mailbox's copy of what was sent, when it keeps one, is read under the same id (GET /v1/mailboxes/{mailbox_id}/messages/{id}); do not rely on that copy being there, or its content being readable, as soon as 202 returns. 202 does not mean delivered: delivery to each recipient happens afterwards, and can still be delayed or fail.

Bcc. Each address in bcc gets the message as a blind copy: no recipient sees it, and the message delivered to anyone has no Bcc field. Only the mailbox's own copy shows them (bcc on getMessage). A Bcc recipient is a recipient like any other: it counts toward the limits and allowances below, and its delivery is reported (and refused) like the others'.

Delivery. What becomes of each recipient is reported on the event feed (GET /v1/events), with the message's id as message_id and the recipient's address (lower-cased) as data.recipient:

  • message.deferred: delivery failed temporarily and is being retried;
  • message.delivered: the recipient's mail system accepted the message. It does not say that anyone read it, or where the recipient's system filed it;
  • message.delivery_failed: delivery failed and is not retried;
  • message.delivery_canceled: the mail service stopped the message before it reached the recipient, because sending was suspended for abuse after the message was accepted; it will not be sent. It still counts toward the monthly email limit.

A recipient gets each of these at most once, in this order: deferred (if at all), then one of the other three, which is final (should the mail system still report a delivery or a failure for a canceled recipient, that report is what stands). A recipient with none of them has no known outcome yet. Some receiving systems never report back, so a recipient can stay that way. Deferral and failure events carry data.status, the mail status code (such as 5.1.1) when there is one, and data.reason, its category: recipient_address (no such address), recipient_mailbox (for example full or disabled), mail_system, network, protocol, content, policy, refused_temporarily or other. Outcomes keep being reported when the mailbox is suspended after sending.

Recipients refused when the message is submitted. The mail system can refuse a recipient at once, when Box submits the message. Such a recipient is never tried, so Box reports it straight away: in the 202 response's refused list (present only then), as failed among the message's recipients, and with a message.delivery_failed event. A refusal for now (a 4.x.x status) has the reason refused_temporarily: the address is not known to be bad, and it does not count toward the monthly email limit. Nothing retries it: to try those recipients again, send to them later with a new key (retrying with the same key returns the same 202 and sends nothing). The recipients not in refused were accepted for delivery, which does not mean they were delivered. If the mail system refuses every recipient for now, nothing is sent at all: see messages.send_unavailable below.

Idempotency. Every request carries an Idempotency-Key of your choice, such as a random value, unique per message you mean to send; keys belong to your customer. Retrying with the same key and the same request never sends the message twice and never counts it twice:

  • once it is sent, a retry returns the same 202 response, with the same id;
  • while the first request is still sending it, a retry gets 409 idempotency.in_progress: retry after Retry-After seconds;
  • a different request under a key already used gets 422 idempotency.key_reused and sends nothing.

A request refused before anything is sent (401, 403, 404, 413, 429, or a 422 other than idempotency.key_reused and messages.send_rejected) does not use up its key.

A suspended mailbox (status: suspended) sends nothing: every request, a retry of an earlier one included, is refused with 403 mailboxes.suspended and leaves its key as it was. Once the mailbox is active again, the same key answers as it would have before.

Sending suspended. While the mail service has suspended sending for the account, for the mailbox's domain, for the mailbox itself or for the credential making the request, every send from it, a retry of an earlier one included, is refused with 403 messages.sending_suspended (params.scope: account, domain, mailbox or credential), and leaves its key as it was: nothing is sent or counted. Messages already accepted may also be stopped before delivery (their recipients become canceled). Retrying does not help; it lasts until the mail service lifts it. Incoming mail keeps arriving and everything stays readable. It is separate from the mailbox's own suspension, from its domain's DNS and from the account's payment: fixing one of those does not lift it, nor does lifting it change them.

A mailbox whose domain is not active sends nothing in the same way: every request is refused with 403 mailboxes.domain_inactive and leaves its key as it was. A domain is active once its ownership is verified, it is set up for mail and a DNS check has found its records as required; it sends again only once a check finds its sending records as required after its ownership was verified again (mail_readiness.outbound not unverified). Meanwhile its mailboxes and their messages stay readable, and incoming mail is deferred only while its ownership is not verified or it is not set up (mail_readiness.inbound).

A domain whose sending records are wrong. When checks over at least 30 minutes, each confirmed by a second, independent DNS lookup, find the domain's sending records (SPF, and the DKIM record Box signs with) wrong (mail_readiness.outbound invalid, mail_readiness.sending suspended), every send from its mailboxes is refused with 403 domains.outbound_dns_invalid and leaves its key as it was: nothing is sent or counted. Fix the records: Box checks the domain again every 5 minutes, and a check (checkDomainDns too) that finds them as required, confirmed by the second lookup, lets its mailboxes send again. A DNS that does not answer never suspends a domain and never lifts a suspension. If Box has not been able to confirm the sending records for 7 days because the DNS did not answer (mail_readiness.sending unconfirmed), sends are refused for now with 503 domains.outbound_dns_unconfirmed and Retry-After, until a check finds them as required. Either way incoming mail keeps arriving, everything stays readable, and messages already accepted are still delivered. It is separate from a suspension by the mail service, from the mailbox's own suspension and from the account's payment: fixing the DNS does not lift those, nor does lifting them change it.

When a send fails, the error says whether the message was sent:

ErrorSent?What to do
422 messages.send_rejectedNoThe message was refused. Change it before trying again.
503 messages.send_unavailableNoRetry later, with the same key or a new one.
503 messages.send_unconfirmedUnknownRetry later with the same key. Box finds out whether it was sent, and never sends it a second time under that key.
503 storage.unavailable, 500 server.errorUnknownRetry with the same key.

messages.send_unavailable with params.recipients means the mail system refused every recipient for now (params.status: the most common mail status code, such as 4.4.5). Nothing was sent or counted, and the key is unused: send it again later with the same key. Its Retry-After is only a suggestion; no one knows when the mail system will accept it.

messages.send_unconfirmed gives the message's id in params.message_id: the id it has if it was sent. If retries keep answering messages.send_unconfirmed, Box cannot confirm that it was sent, and it most likely was not. Sending it again needs a new key, and may deliver it twice.

Attachments. Each attachment is sent with its filename and content_type, and its bytes exactly as content_base64 gives them. A text attachment (a text/ content_type) may also name its charset: the character set its bytes are in, sent as the attachment's charset. Box never converts the content and never guesses a character set: without charset, none is stated and recipients read the text as US-ASCII, so name the character set of any other text (for a file downloaded with getAttachment, the charset of its Content-Type). A charset that is not plainly a character set's name, or one on an attachment of another type, is 422 request.invalid.

Limits. Each recipient in to, cc and bcc counts once toward the plan's monthly email limit (403 entitlements.email_limit), except one the mail system refused for now when the message was submitted (refused_temporarily), and a message not sent at all. Nothing can be sent while the customer's stored mail is at its plan's storage limit (403 entitlements.storage_limit). A Box address that is not accepting mail is refused with 422 messages.recipient_unavailable, listed in params.recipients. At most 50 addresses each in to, cc and bcc, 100 in all, each address once across them, 10 attachments of at most 10 MiB in total, and 24 MiB per request (413 request.too_large).

Rate. Each mailbox may send so many messages at once and then so many an hour (send.messages), and the account may send to so many recipients (each in to, cc and bcc) at once and then so many an hour (send.recipients): see 429. Past either, the message is refused with 429 auth.rate_limited before anything is sent or counted, and its key is unused: send it again after Retry-After seconds (the longer wait when both are used up), not in a loop. The mail service may also limit the recipients sent to from each domain in an hour (send.domain.recipients) and in a day (send.domain.recipients.daily, params.scope domain) and the account's recipients in a day (send.recipients.daily), refused the same way. A retry of a message already sent, or still being sent, is answered as above whatever is left of these allowances, and uses none of them. A message refused by the mail system, or not sent because it could not be reached, has used its share.

Requires messages:send.

Parameters

NameInTypeDescription
mailbox_id requiredpathstring
Idempotency-Key requiredheaderstring

Identifies this message across retries: 1 to 200 visible ASCII characters, unique per message you send (a random value, for example).

min length 1 · max length 200 · pattern ^[\x21-\x7e]{1,200}$

Request body application/json required

OutgoingMessage

FieldTypeDescription
attachmentsarray of OutgoingAttachment

Files to attach, at most 10 MiB in total.

max items 10

bccarray of string (email)

Addresses to send a blind copy to: they get the message, and no recipient sees them (only the mailbox's own copy shows them). Each recipient may appear only once across to, cc and bcc, at most 100 in all.

max items 50

ccarray of string (email)

Addresses to copy. Each recipient may appear only once across to, cc and bcc.

max items 50

subject requiredstring

max length 998

text requiredstring

The plain-text body.

max length 1000000

to requiredarray of string (email)

Recipient addresses.

min items 1 · max items 50

Responses

202 application/json SubmittedMessage

Successful Response

401 application/json Error

No usable credential: auth.missing_token or auth.invalid_token.

402 application/json Error

billing.payment_required: the account's plan is paid for through Stripe and its payment is not in good standing (params.status: past_due, unpaid, paused or canceled), so new mailboxes, domains and outgoing mail are on hold. Nothing was created or sent. Existing mail is kept, mail keeps arriving, and mailboxes and domains can still be read and deleted. The account's owner resolves it.

403 application/json Error

The credential lacks messages:send (auth.insufficient_scope), the mailbox is suspended (mailboxes.suspended), sending is suspended for its account, domain, itself or the credential (messages.sending_suspended), its domain is not active (mailboxes.domain_inactive), its domain's sending records are wrong (domains.outbound_dns_invalid), or the plan's limits are reached: entitlements.email_limit, entitlements.storage_limit.

404 application/json Error

mailboxes.not_found: no such mailbox, or none this credential can reach.

409 application/json Error

idempotency.in_progress: a request with this Idempotency-Key is sending the message right now. Retry after Retry-After seconds.

413 application/json Error

request.too_large: the request body is over 24 MiB.

422 application/json Error

Invalid body or Idempotency-Key (request.invalid), the key already used for a different request (idempotency.key_reused), a Box recipient not accepting mail (messages.recipient_unavailable), or the message refused (messages.send_rejected, not sent).

429 application/json Error

auth.rate_limited with params.limit send.messages and params.scope mailbox: the mailbox's allowance of messages sent (60 per hour, up to 20 at once) is used up for now; or with params.limit send.recipients and params.scope account: the account's allowance of recipients, each in to, cc and bcc (200 per hour, up to 100 at once), is used up for now. The mail service may also refuse it with params.limit naming one of its recipient limits, each recipient one unit: send.domain.recipients (200 per hour, up to 100 at once), send.recipients.daily (1000 per 24 hours, up to 1000 at once), send.domain.recipients.daily (500 per 24 hours, up to 500 at once), params.scope domain or account. Both are shared by this API and the MCP server and by all the account's credentials, and a retry of a message already sent or being sent is never refused for them. Nothing was sent or counted, and the Idempotency-Key is unused. Retry after Retry-After seconds (the longer wait when both are used up), not before. auth.rate_limited with empty params: too many of the account's requests are in progress at once. Nothing was done. Retry after Retry-After seconds (1).

500 application/json Error

Unexpected error: server.error.

503 application/json Error

Not sent: messages.send_unavailable (with params.recipients and params.status when the mail system refused every recipient for now), domains.outbound_dns_unconfirmed (the domain's sending records could not be confirmed for 7 days). Maybe sent: messages.send_unconfirmed, storage.unavailable. Retry with the same Idempotency-Key.

Get a message

GET /v1/mailboxes/{mailbox_id}/messages/{message_id} getMessage

messages:read

Requires messages:read.

One message of the mailbox, with its content.

  • text is the plain-text body and html the HTML body. Either is null when the message has none.
  • attachments describes the message's attachments, each with the id that getAttachment downloads its content by. An attachment's id stays the same on every read of the message.
  • recipients, for a message the mailbox sent, is each recipient's delivery state as it is now: pending, deferred, delivered, failed or canceled. The event feed reports each change (message.deferred, message.delivered, message.delivery_failed, message.delivery_canceled); this is where they stand. It is null for mail the mailbox received, and for sent mail Box has no delivery state for.
  • bcc, for a message the mailbox sent through Box, is the addresses it was sent to as blind copies. It is empty for every other message: mail the mailbox received never shows a Bcc, whatever the message says.
  • A message id belongs to one mailbox. Under any other mailbox it is 404 messages.not_found, as is a message that has been permanently deleted. A message deleted the ordinary way (deleteMessage) is in the trash, folder: trash, and reads as before.
  • A message in Junk is 404 messages.not_found for a credential without junk:read. With it, the message reads as any other, with junk set (see listJunkMessages); otherwise junk is null.

Requires messages:read.

Parameters

NameInTypeDescription
mailbox_id requiredpathstring
message_id requiredpathstring

Responses

200 application/json MessageDetail

Successful Response

401 application/json Error

No usable credential: auth.missing_token or auth.invalid_token.

403 application/json Error

The credential lacks the operation's scope: auth.insufficient_scope.

404 application/json Error

mailboxes.not_found: no such mailbox, or none this credential can reach. messages.not_found: no such message in this mailbox (or, for a credential without junk:read, a message in Junk).

422 application/json Error

Invalid parameters: request.invalid.

429 application/json Error

auth.rate_limited with params.limit read.mail and params.scope account: the account's read.mail allowance (120 requests per minute, up to 60 at once), shared by this API and the MCP server and by all the account's credentials, is used up for now. Nothing was done. Retry after Retry-After seconds, not before. auth.rate_limited with empty params: too many of the account's requests are in progress at once. Nothing was done. Retry after Retry-After seconds (1).

500 application/json Error

Unexpected error: server.error.

503 application/json Error

Temporarily unavailable, retry later: storage.unavailable.

Mark a message read or unread

PATCH /v1/mailboxes/{mailbox_id}/messages/{message_id} updateMessage

messages:write

Requires messages:write.

Marks the message as read ("is_read": true) or unread (false), and returns the message as it is now.

  • Setting the value the message already has changes nothing and succeeds, so repeating the request is safe.
  • Only the message named is changed. The event feed does not report the change.
  • A suspended mailbox stays readable, and its messages can be marked too.
  • A message id belongs to one mailbox. Under any other mailbox it is 404 messages.not_found, as is a message that has been permanently deleted, and a message in Junk for a credential without junk:read.

Only is_read can be changed. Requires messages:write.

Parameters

NameInTypeDescription
mailbox_id requiredpathstring
message_id requiredpathstring

Request body application/json required

MessageUpdate

FieldTypeDescription
is_read requiredboolean

true marks the message as read, false as unread.

Responses

200 application/json Message

Successful Response

401 application/json Error

No usable credential: auth.missing_token or auth.invalid_token.

403 application/json Error

The credential lacks the operation's scope: auth.insufficient_scope.

404 application/json Error

mailboxes.not_found: no such mailbox, or none this credential can reach. messages.not_found: no such message in this mailbox (or, for a credential without junk:read, a message in Junk).

413 application/json Error

request.too_large: the request body is too large.

422 application/json Error

Invalid parameters: request.invalid.

429 application/json Error

auth.rate_limited with params.limit write.mail and params.scope account: the account's write.mail allowance (120 requests per minute, up to 60 at once), shared by this API and the MCP server and by all the account's credentials, is used up for now. Nothing was done. Retry after Retry-After seconds, not before. auth.rate_limited with empty params: too many of the account's requests are in progress at once. Nothing was done. Retry after Retry-After seconds (1).

500 application/json Error

Unexpected error: server.error.

503 application/json Error

Temporarily unavailable, retry later: storage.unavailable.

Delete a message (move it to the trash)

DELETE /v1/mailboxes/{mailbox_id}/messages/{message_id} deleteMessage

messages:delete

Requires messages:delete.

Deletes a message the ordinary way: moves it to the mailbox's trash. Nothing is destroyed:

  • the message keeps its id, and can still be listed, searched and read, with folder: "trash";
  • the trash becomes its only folder, wherever it was (inbox, sent, junk, drafts);
  • it still counts toward the account's stored mail (getUsage) until it is permanently deleted: permanentlyDeleteMessage, or emptyTrash. Mail in the trash may also be permanently deleted automatically after a retention period;
  • restoreMessage moves it back out of the trash.

Deleting a message that is already in the trash succeeds and changes nothing: this never deletes a message permanently, so repeating the request is always safe.

Deleting the mailbox's copy of a message it sent does not recall it or stop its delivery: each recipient's outcome keeps being reported on the event feed. Nothing about the message is announced on the feed.

404 messages.not_found: no such message in this mailbox (a permanently deleted one included, and a message in Junk for a credential without junk:read). 503 storage.unavailable: the stored mail could not be reached, and nothing changed; retry.

Requires messages:delete: messages:write does not allow it. A credential restricted to one mailbox deletes only in that mailbox.

Parameters

NameInTypeDescription
mailbox_id requiredpathstring
message_id requiredpathstring

Responses

204

Successful Response

401 application/json Error

No usable credential: auth.missing_token or auth.invalid_token.

403 application/json Error

The credential lacks messages:delete (auth.insufficient_scope): messages:write does not allow deleting.

404 application/json Error

mailboxes.not_found: no such mailbox, or none this credential can reach. messages.not_found: no such message in this mailbox (or, for a credential without junk:read, a message in Junk).

409 application/json Error

messages.operation_in_progress: another deletion in this mailbox is in progress; retry after Retry-After seconds (5).

422 application/json Error

Invalid parameters: request.invalid.

429 application/json Error

auth.rate_limited with params.limit write.mail and params.scope account: the account's write.mail allowance (120 requests per minute, up to 60 at once), shared by this API and the MCP server and by all the account's credentials, is used up for now. Nothing was done. Retry after Retry-After seconds, not before. auth.rate_limited with empty params: too many of the account's requests are in progress at once. Nothing was done. Retry after Retry-After seconds (1).

500 application/json Error

Unexpected error: server.error.

503 application/json Error

storage.unavailable: the stored mail could not be reached; nothing changed. Retry.

Download an attachment

GET /v1/mailboxes/{mailbox_id}/messages/{message_id}/attachments/{attachment_id} getAttachment

messages:read

Requires messages:read.

One attachment of a message, by the id the message's read (getMessage) lists it with: its content, exactly as it was sent (decoded from its transfer encoding, otherwise untouched), as the response body.

  • Content-Type is the attachment's media type if it is plainly one (lower-cased, with the charset the message declares for a text/ type), and application/octet-stream otherwise. The sender chose it: it may not describe the content.
  • Content-Disposition is always attachment, with the sender's file name made safe (filename, ASCII, and filename*, UTF-8); an attachment without one is named attachment-<n>: the n-th attachment of the message.
  • Content-Length is the content's size. The response is never cached (Cache-Control: no-store) and carries X-Content-Type-Options: nosniff and Content-Security-Policy: default-src 'none'; sandbox: handle the content as untrusted data.
  • An attachment larger than the limit (by default 25 MiB) is 422 attachments.too_large, with its size (params.size; at least params.max_bytes + 1 when only its bytes showed it) and the limit (params.max_bytes), refused before it is downloaded when its stated size is over.
  • (rate limiting) Each download counts its bytes toward the account's attachment-byte allowance (attachment.bytes, see 429), besides the number of downloads: the size getMessage lists, before anything is downloaded (no room: 429, nothing downloaded), and any bytes that turn out to be beyond it once it is. A download stopped by the caller still counts what was downloaded.
  • An attachment id names one attachment of one message: under another message, or one the message does not list, it is 404 attachments.not_found. A message that is not found (another mailbox's, permanently deleted) is 404 messages.not_found, as for getMessage; a message in the trash reads as before. So is a message in Junk, for a credential without junk:read.

Requires messages:read.

Parameters

NameInTypeDescription
mailbox_id requiredpathstring
message_id requiredpathstring
attachment_id requiredpathstring

Responses

200 application/octet-stream string (binary)

The attachment's content, as raw bytes.

401 application/json Error

No usable credential: auth.missing_token or auth.invalid_token.

403 application/json Error

The credential lacks the operation's scope: auth.insufficient_scope.

404 application/json Error

mailboxes.not_found: no such mailbox, or none this credential can reach. messages.not_found: no such message in this mailbox (or, for a credential without junk:read, a message in Junk). attachments.not_found: the message has no attachment with this id.

422 application/json Error

attachments.too_large: the attachment is larger than the limit (params.size, params.max_bytes).

429 application/json Error

auth.rate_limited with params.limit attachments and params.scope account: the account's attachments allowance (30 requests per minute, up to 15 at once), shared by this API and the MCP server and by all the account's credentials, is used up for now. Nothing was done. Retry after Retry-After seconds, not before. auth.rate_limited with params.limit attachment.bytes and params.scope account: the account's allowance of attachment bytes downloaded from storage (1 GiB per hour, up to 256 MiB at once), shared by downloading attachments and forwarding messages with theirs, through this API and the MCP server, is used up for now. Nothing was downloaded. Retry after Retry-After seconds, not before. auth.rate_limited with empty params: too many of the account's requests are in progress at once. Nothing was done. Retry after Retry-After seconds (1).

500 application/json Error

Unexpected error: server.error.

503 application/json Error

Temporarily unavailable, retry later: storage.unavailable.

Forward a message

POST /v1/mailboxes/{mailbox_id}/messages/{message_id}/forward forwardMessage

messages:read messages:send Idempotency-Key required

Requires messages:read and messages:send.

Forwards a message of the mailbox to the recipients you give (to, cc, bcc), from the mailbox's address. The forward is a new message, not part of the message's conversation:

  • Subject: Fwd: and the message's subject, unless it already starts with Fwd: or Fw:; or the subject you give.
  • Text: your text (it may be empty), then a forwarded-message block with the message's sender, date, subject and recipients, then the message's text. A message with only an HTML body is forwarded as text made from it.
  • Attachments: your attachments, then the message's own attachments, unless include_original_attachments is false: their bytes as they are, with their file names, media types and, for a text one, the charset the message declares. To send only some of them, list their ids (as getMessage lists them) in original_attachment_ids: they go in the message's order, and [] sends none. An id the message does not list is 422 messages.original_attachment_unknown (params.original_attachment_ids names them); nothing is downloaded or sent. Together at most 10 files and 10 MiB (422 messages.attachments_too_large: forward fewer of the message's attachments, or fewer of your own). Downloading the message's attachments counts their bytes toward the account's attachment-byte allowance, as downloading them with getAttachment does: past it, the forward is 429 auth.rate_limited with params.limit attachment.bytes, before anything is downloaded or sent (forward it later, or without the message's attachments). Those downloaded count even if the forward is then refused.

Each address once across to, cc and bcc, at most 50 in each and 100 in all (422 request.invalid otherwise), as for sendMessage; Bcc as for sendMessage. A message whose text is too long to forward (over 1,000,000 characters) is 422 messages.forward_too_large. A Box address that is not accepting mail is refused as for sendMessage (422 messages.recipient_unavailable).

Sending. It is sent like any message (sendMessage), under the same rules: an Idempotency-Key names it (keys are shared with sendMessage: a key used for one message cannot be used for another), each recipient counts toward the plan's monthly email limit, it counts toward the same sending allowances (429 auth.rate_limited, send.messages, send.recipients), and the same errors say whether it was sent. A retry of one that was sent answers the same 202, with the same id, even if the message it came from has been deleted since. The 202 names the new message: read it (getMessage) to see exactly what was sent, and to whom. Your attachments are sent as sendMessage sends them (a text one may name its charset).

The message is one of the mailbox's (messages.not_found otherwise, like a message of another mailbox), in any folder but Junk: nothing is sent from Junk (409 messages.in_junk, or 404 messages.not_found for a credential without junk:read).

Requires messages:read and messages:send.

Parameters

NameInTypeDescription
mailbox_id requiredpathstring
message_id requiredpathstring
Idempotency-Key requiredheaderstring

Identifies this message across retries: 1 to 200 visible ASCII characters, unique per message you send (a random value, for example).

min length 1 · max length 200 · pattern ^[\x21-\x7e]{1,200}$

Request body application/json required

ForwardMessage

FieldTypeDescription
attachmentsarray of OutgoingAttachment

New files to attach, in addition to the forwarded message's own attachments. With those, at most 10 files and 10 MiB in total.

max items 10

bccarray of string (email)

Addresses to send a blind copy to: they get the message, and no recipient sees them (only the mailbox's own copy shows them). Each recipient may appear only once across to, cc and bcc, at most 100 in all.

max items 50

ccarray of string (email)

Addresses to copy. Each recipient may appear only once across to, cc and bcc.

max items 50

include_original_attachmentsboolean

Whether the forwarded message's own attachments are attached too (after attachments). false forwards its text only.

default true

original_attachment_idsarray of string

Which of the forwarded message's own attachments go, by the id its read lists each with (att_...), each once: they are attached in the message's order. [] sends none of them. Leave it out to send them all. Not with include_original_attachments: false. An id the message does not list is 422 messages.original_attachment_unknown.

max items 10

subjectstring

The forward's subject, instead of Fwd: and the message's. Leave it out to keep Box's.

max length 998

textstring

A note, sent above the forwarded message. May be empty.

max length 1000000 · default ""

to requiredarray of string (email)

Recipient addresses.

min items 1 · max items 50

Responses

202 application/json SubmittedMessage

Successful Response

401 application/json Error

No usable credential: auth.missing_token or auth.invalid_token.

402 application/json Error

billing.payment_required: the account's plan is paid for through Stripe and its payment is not in good standing (params.status: past_due, unpaid, paused or canceled), so new mailboxes, domains and outgoing mail are on hold. Nothing was created or sent. Existing mail is kept, mail keeps arriving, and mailboxes and domains can still be read and deleted. The account's owner resolves it.

403 application/json Error

The credential lacks messages:read or messages:send (auth.insufficient_scope), the mailbox is suspended (mailboxes.suspended), sending is suspended for its account, domain, itself or the credential (messages.sending_suspended), its domain is not active (mailboxes.domain_inactive), its domain's sending records are wrong (domains.outbound_dns_invalid), or the plan's limits are reached: entitlements.email_limit, entitlements.storage_limit.

404 application/json Error

mailboxes.not_found: no such mailbox, or none this credential can reach. messages.not_found: no such message in this mailbox (or, for a credential without junk:read, a message in Junk).

409 application/json Error

idempotency.in_progress: a request with this Idempotency-Key is sending the message right now. Retry after Retry-After seconds. messages.in_junk: the message is in Junk, and nothing is sent from Junk; mark it as not spam first (markNotSpam).

413 application/json Error

request.too_large: the request body is over 24 MiB.

422 application/json Error

Invalid body or Idempotency-Key (request.invalid), the key already used for a different request (idempotency.key_reused), a Box recipient not accepting mail (messages.recipient_unavailable), or the message refused (messages.send_rejected, not sent). Too many or too large attachments with the message's own (messages.attachments_too_large), an attachment id the message does not list (messages.original_attachment_unknown), or the message's text too long to forward (messages.forward_too_large).

429 application/json Error

auth.rate_limited with params.limit send.messages and params.scope mailbox: the mailbox's allowance of messages sent (60 per hour, up to 20 at once) is used up for now; or with params.limit send.recipients and params.scope account: the account's allowance of recipients, each in to, cc and bcc (200 per hour, up to 100 at once), is used up for now. The mail service may also refuse it with params.limit naming one of its recipient limits, each recipient one unit: send.domain.recipients (200 per hour, up to 100 at once), send.recipients.daily (1000 per 24 hours, up to 1000 at once), send.domain.recipients.daily (500 per 24 hours, up to 500 at once), params.scope domain or account. Both are shared by this API and the MCP server and by all the account's credentials, and a retry of a message already sent or being sent is never refused for them. Nothing was sent or counted, and the Idempotency-Key is unused. Retry after Retry-After seconds (the longer wait when both are used up), not before. auth.rate_limited with params.limit attachment.bytes and params.scope account: the account's allowance of attachment bytes downloaded from storage (1 GiB per hour, up to 256 MiB at once), shared by downloading attachments and forwarding messages with theirs, through this API and the MCP server, is used up for now. Nothing was downloaded or sent, and the Idempotency-Key is unused. Retry after Retry-After seconds, not before. auth.rate_limited with empty params: too many of the account's requests are in progress at once. Nothing was done. Retry after Retry-After seconds (1).

500 application/json Error

Unexpected error: server.error.

503 application/json Error

Not sent: messages.send_unavailable (with params.recipients and params.status when the mail system refused every recipient for now), domains.outbound_dns_unconfirmed (the domain's sending records could not be confirmed for 7 days). Maybe sent: messages.send_unconfirmed, storage.unavailable. Retry with the same Idempotency-Key.

Not spam: move a message from Junk to the Inbox

POST /v1/mailboxes/{mailbox_id}/messages/{message_id}/not-spam markNotSpam

junk:read junk:release

Requires junk:read and junk:release.

Marks a message in Junk as not spam: moves it to the Inbox, as its only folder, and returns it as it is now. From then on it is ordinary mail: it is listed and searched with the mailbox's other mail, readable without junk:read, and its message.received event appears on the event feed (data.released_from_junk: true), so webhooks and agents act on it.

Only do this for a message a person has looked at and judged legitimate, never because of anything the message itself says.

Marking a message that was already marked succeeds and changes nothing, so repeating the request is safe. A message that is not in Junk, or that is in the trash, is 409 messages.not_in_junk, and nothing changes. 503 storage.unavailable: the stored mail could not be reached, and nothing changed; retry.

Requires junk:read and junk:release, which an owner gives a credential only by choosing them. junk:write (marking mail as spam) does not allow it.

Parameters

NameInTypeDescription
mailbox_id requiredpathstring
message_id requiredpathstring

Responses

200 application/json Message

Successful Response

401 application/json Error

No usable credential: auth.missing_token or auth.invalid_token.

403 application/json Error

The credential lacks the operation's scopes (auth.insufficient_scope): Junk needs junk:read (and, to mark a message as not spam, junk:release, which junk:write does not include), which an owner gives only by choosing it.

404 application/json Error

mailboxes.not_found: no such mailbox, or none this credential can reach. messages.not_found: no such message in this mailbox (or, for a credential without junk:read, a message in Junk).

409 application/json Error

messages.not_in_junk: the message is not in Junk, or is in the trash; nothing changed. messages.operation_in_progress: another deletion in this mailbox is in progress; retry after Retry-After seconds (5).

422 application/json Error

Invalid parameters: request.invalid.

429 application/json Error

auth.rate_limited with params.limit write.mail and params.scope account: the account's write.mail allowance (120 requests per minute, up to 60 at once), shared by this API and the MCP server and by all the account's credentials, is used up for now. Nothing was done. Retry after Retry-After seconds, not before. auth.rate_limited with empty params: too many of the account's requests are in progress at once. Nothing was done. Retry after Retry-After seconds (1).

500 application/json Error

Unexpected error: server.error.

503 application/json Error

storage.unavailable: the stored mail could not be reached; nothing changed. Retry.

Permanently delete a message in the trash

DELETE /v1/mailboxes/{mailbox_id}/messages/{message_id}/permanent permanentlyDeleteMessage

messages:destroy

Requires messages:destroy.

Permanently deletes one message that is in the trash. It cannot be recovered: its content is gone, the storage it used is free (getUsage shows that once the mailbox is measured, normally at once), and its id is not found from then on (404 messages.not_found) and never used again.

Only a message in the trash, and in no other folder, is deleted: where the message is is checked at the moment of the request, and any other message is refused and left unchanged (409 messages.not_in_trash). Delete it first (deleteMessage): that moves it to the trash. Deleting a message never deletes it permanently on its own, even when it is already in the trash.

What happened stays on record. Its events stay on the event feed, a received message's from and subject removed (data.redacted: true). For a message the mailbox sent, permanently deleting its copy does not recall it or stop its delivery: each recipient's outcome keeps being reported. What the message counted toward the plan (emails sent or received this month) does not change.

Repeating the request after it succeeded answers 404 messages.not_found: the message is gone, as is a message in Junk for a credential without junk:read. 503 storage.unavailable: the stored mail could not be reached, and nothing was deleted; retry.

Requires messages:destroy: messages:delete does not allow it. An application deleting for a person should ask that person first; the credential's scope is standing authorization, not that person's approval.

Parameters

NameInTypeDescription
mailbox_id requiredpathstring
message_id requiredpathstring

Responses

204

Successful Response

401 application/json Error

No usable credential: auth.missing_token or auth.invalid_token.

403 application/json Error

The credential lacks messages:destroy (auth.insufficient_scope): messages:delete (moving to the trash) does not allow permanent deletion.

404 application/json Error

mailboxes.not_found: no such mailbox, or none this credential can reach. messages.not_found: no such message in this mailbox (or, for a credential without junk:read, a message in Junk).

409 application/json Error

messages.not_in_trash: the message is not in the trash, or is in another folder too; nothing was deleted. messages.operation_in_progress: another deletion in this mailbox is in progress; retry after Retry-After seconds (5).

422 application/json Error

Invalid parameters: request.invalid.

429 application/json Error

auth.rate_limited with params.limit write.mail and params.scope account: the account's write.mail allowance (120 requests per minute, up to 60 at once), shared by this API and the MCP server and by all the account's credentials, is used up for now. Nothing was done. Retry after Retry-After seconds, not before. auth.rate_limited with empty params: too many of the account's requests are in progress at once. Nothing was done. Retry after Retry-After seconds (1).

500 application/json Error

Unexpected error: server.error.

503 application/json Error

storage.unavailable: the stored mail could not be reached; nothing was deleted. Retry.

Reply to a message

POST /v1/mailboxes/{mailbox_id}/messages/{message_id}/reply replyToMessage

messages:read messages:send Idempotency-Key required

Requires messages:read and messages:send.

Replies to a message of the mailbox, from the mailbox's address. You give the reply's text and attachments; Box works out the rest from the message:

  • To: the message's reply_to addresses or, if it has none, its sender (from). A reply to a message the mailbox sent itself goes to that message's to.
  • Subject: Re: and the message's subject, unless it already starts with Re:.
  • Conversation: the reply names the message in its In-Reply-To and References headers, so mail programs show it in the message's conversation.

The message is not quoted: the reply says exactly text. Read the message first (getMessage) to see where the reply goes.

An address the reply would go to that Box cannot send to is refused, never left out: 422 messages.recipient_unsupported, with params.headers naming where the message has it (from, reply_to or to); nothing is sent. Name the reply's to yourself instead (below). A delivery report is not replied to (422 messages.not_repliable).

Choosing the recipients and subject. Give to, cc or subject to use your own instead of Box's, and bcc to add blind copies (as for sendMessage); leave a field out to keep Box's. A field you give replaces Box's: cc: [] sends no Cc. An address you give is left out of the fields Box works out, so it gets the reply once, where you put it. Each address once across your own to, cc and bcc, at most 50 in each (422 request.invalid otherwise). An address Box cannot send to matters only in the message's headers a field you left to Box comes from. The Cc moves up to To when nothing is left for To only when you gave neither. A reply with nothing in To is 422 messages.no_recipients; more than 50 addresses in a field, or more than 100 in all, 422 messages.too_many_recipients (params.limit, or params.total_limit). Whatever you choose, the reply names the message (In-Reply-To, References); you cannot set those. A subject is sent as you give it, under sendMessage's rules; but mail programs, Box's own conversations (getThread) included, start a new conversation for a reply whose subject differs from the message's.

Sending. It is sent like any message (sendMessage), under the same rules: an Idempotency-Key names it (keys are shared with sendMessage: a key used for one message cannot be used for another), each recipient counts toward the plan's monthly email limit, it counts toward the same sending allowances (429 auth.rate_limited, send.messages, send.recipients), and the same errors say whether it was sent. A retry of one that was sent answers the same 202, with the same id, even if the message it came from has been deleted since. The 202 names the new message: read it (getMessage) to see exactly what was sent, and to whom. Your attachments are sent as sendMessage sends them (a text one may name its charset).

The message is one of the mailbox's (messages.not_found otherwise, like a message of another mailbox), in any folder but Junk: nothing is sent from Junk (409 messages.in_junk, or 404 messages.not_found for a credential without junk:read).

Requires messages:read and messages:send.

Parameters

NameInTypeDescription
mailbox_id requiredpathstring
message_id requiredpathstring
Idempotency-Key requiredheaderstring

Identifies this message across retries: 1 to 200 visible ASCII characters, unique per message you send (a random value, for example).

min length 1 · max length 200 · pattern ^[\x21-\x7e]{1,200}$

Request body application/json required

ReplyMessage

FieldTypeDescription
attachmentsarray of OutgoingAttachment

Files to attach, at most 10 MiB in total.

max items 10

bccarray of string (email)

Addresses to send a blind copy to: they get the message, and no recipient sees them (only the mailbox's own copy shows them). Each recipient may appear only once across to, cc and bcc, at most 100 in all.

max items 50

ccarray of string (email)

The reply's Cc, instead of Box's (none for a reply; the message's Cc for a reply to all). [] sends no Cc. Leave it out to keep Box's.

max items 50

subjectstring

The reply's subject, instead of Re: and the message's. The reply still names the message (In-Reply-To, References), but a changed subject starts a conversation of its own in the mailbox. Leave it out to keep Box's.

max length 998

text requiredstring

The reply's plain-text body, exactly as it is sent: the message replied to is not quoted in it.

max length 1000000

toarray of string (email)

The reply's To, instead of the one Box works out from the message. Leave it out to keep Box's.

min items 1 · max items 50

Responses

202 application/json SubmittedMessage

Successful Response

401 application/json Error

No usable credential: auth.missing_token or auth.invalid_token.

402 application/json Error

billing.payment_required: the account's plan is paid for through Stripe and its payment is not in good standing (params.status: past_due, unpaid, paused or canceled), so new mailboxes, domains and outgoing mail are on hold. Nothing was created or sent. Existing mail is kept, mail keeps arriving, and mailboxes and domains can still be read and deleted. The account's owner resolves it.

403 application/json Error

The credential lacks messages:read or messages:send (auth.insufficient_scope), the mailbox is suspended (mailboxes.suspended), sending is suspended for its account, domain, itself or the credential (messages.sending_suspended), its domain is not active (mailboxes.domain_inactive), its domain's sending records are wrong (domains.outbound_dns_invalid), or the plan's limits are reached: entitlements.email_limit, entitlements.storage_limit.

404 application/json Error

mailboxes.not_found: no such mailbox, or none this credential can reach. messages.not_found: no such message in this mailbox (or, for a credential without junk:read, a message in Junk).

409 application/json Error

idempotency.in_progress: a request with this Idempotency-Key is sending the message right now. Retry after Retry-After seconds. messages.in_junk: the message is in Junk, and nothing is sent from Junk; mark it as not spam first (markNotSpam).

413 application/json Error

request.too_large: the request body is over 24 MiB.

422 application/json Error

Invalid body or Idempotency-Key (request.invalid), the key already used for a different request (idempotency.key_reused), a Box recipient not accepting mail (messages.recipient_unavailable), or the message refused (messages.send_rejected, not sent). An address the reply would go to that Box cannot send to (messages.recipient_unsupported), no one to reply to (messages.no_recipients), too many addresses (messages.too_many_recipients), or a delivery report (messages.not_repliable).

429 application/json Error

auth.rate_limited with params.limit send.messages and params.scope mailbox: the mailbox's allowance of messages sent (60 per hour, up to 20 at once) is used up for now; or with params.limit send.recipients and params.scope account: the account's allowance of recipients, each in to, cc and bcc (200 per hour, up to 100 at once), is used up for now. The mail service may also refuse it with params.limit naming one of its recipient limits, each recipient one unit: send.domain.recipients (200 per hour, up to 100 at once), send.recipients.daily (1000 per 24 hours, up to 1000 at once), send.domain.recipients.daily (500 per 24 hours, up to 500 at once), params.scope domain or account. Both are shared by this API and the MCP server and by all the account's credentials, and a retry of a message already sent or being sent is never refused for them. Nothing was sent or counted, and the Idempotency-Key is unused. Retry after Retry-After seconds (the longer wait when both are used up), not before. auth.rate_limited with empty params: too many of the account's requests are in progress at once. Nothing was done. Retry after Retry-After seconds (1).

500 application/json Error

Unexpected error: server.error.

503 application/json Error

Not sent: messages.send_unavailable (with params.recipients and params.status when the mail system refused every recipient for now), domains.outbound_dns_unconfirmed (the domain's sending records could not be confirmed for 7 days). Maybe sent: messages.send_unconfirmed, storage.unavailable. Retry with the same Idempotency-Key.

Reply to all

POST /v1/mailboxes/{mailbox_id}/messages/{message_id}/reply-all replyAllToMessage

messages:read messages:send Idempotency-Key required

Requires messages:read and messages:send.

Replies to a message of the mailbox and to everyone it was sent to, from the mailbox's address. You give the reply's text and attachments; Box works out the rest from the message:

  • To: the message's reply_to addresses (or, if it has none, its sender, from), and the message's other to addresses. For a message the mailbox sent itself: its to.
  • Cc: the message's cc addresses.
  • Each address once, and never the mailbox's own address. Other addresses of the account stay. Nobody the message was sent to as Bcc gets the reply: Box never reads a message's Bcc to reply.
  • Subject and conversation as for replyToMessage.

The message is not quoted: the reply says exactly text. Read the message first (getMessage) to see who the reply goes to.

An address the reply would go to that Box cannot send to is refused, never left out: 422 messages.recipient_unsupported, with params.headers naming where the message has it (from, reply_to, to or cc); nothing is sent. With no one but the mailbox itself to reply to: 422 messages.no_recipients. A delivery report is not replied to (422 messages.not_repliable).

Choosing the recipients and subject. Give to, cc or subject to use your own instead of Box's, and bcc to add blind copies (as for sendMessage); leave a field out to keep Box's. A field you give replaces Box's: cc: [] sends no Cc. An address you give is left out of the fields Box works out, so it gets the reply once, where you put it. Each address once across your own to, cc and bcc, at most 50 in each (422 request.invalid otherwise). An address Box cannot send to matters only in the message's headers a field you left to Box comes from. The Cc moves up to To when nothing is left for To only when you gave neither. A reply with nothing in To is 422 messages.no_recipients; more than 50 addresses in a field, or more than 100 in all, 422 messages.too_many_recipients (params.limit, or params.total_limit). Whatever you choose, the reply names the message (In-Reply-To, References); you cannot set those. A subject is sent as you give it, under sendMessage's rules; but mail programs, Box's own conversations (getThread) included, start a new conversation for a reply whose subject differs from the message's.

Sending. It is sent like any message (sendMessage), under the same rules: an Idempotency-Key names it (keys are shared with sendMessage: a key used for one message cannot be used for another), each recipient counts toward the plan's monthly email limit, it counts toward the same sending allowances (429 auth.rate_limited, send.messages, send.recipients), and the same errors say whether it was sent. A retry of one that was sent answers the same 202, with the same id, even if the message it came from has been deleted since. The 202 names the new message: read it (getMessage) to see exactly what was sent, and to whom. Your attachments are sent as sendMessage sends them (a text one may name its charset).

The message is one of the mailbox's (messages.not_found otherwise, like a message of another mailbox), in any folder but Junk: nothing is sent from Junk (409 messages.in_junk, or 404 messages.not_found for a credential without junk:read).

Requires messages:read and messages:send.

Parameters

NameInTypeDescription
mailbox_id requiredpathstring
message_id requiredpathstring
Idempotency-Key requiredheaderstring

Identifies this message across retries: 1 to 200 visible ASCII characters, unique per message you send (a random value, for example).

min length 1 · max length 200 · pattern ^[\x21-\x7e]{1,200}$

Request body application/json required

ReplyMessage

FieldTypeDescription
attachmentsarray of OutgoingAttachment

Files to attach, at most 10 MiB in total.

max items 10

bccarray of string (email)

Addresses to send a blind copy to: they get the message, and no recipient sees them (only the mailbox's own copy shows them). Each recipient may appear only once across to, cc and bcc, at most 100 in all.

max items 50

ccarray of string (email)

The reply's Cc, instead of Box's (none for a reply; the message's Cc for a reply to all). [] sends no Cc. Leave it out to keep Box's.

max items 50

subjectstring

The reply's subject, instead of Re: and the message's. The reply still names the message (In-Reply-To, References), but a changed subject starts a conversation of its own in the mailbox. Leave it out to keep Box's.

max length 998

text requiredstring

The reply's plain-text body, exactly as it is sent: the message replied to is not quoted in it.

max length 1000000

toarray of string (email)

The reply's To, instead of the one Box works out from the message. Leave it out to keep Box's.

min items 1 · max items 50

Responses

202 application/json SubmittedMessage

Successful Response

401 application/json Error

No usable credential: auth.missing_token or auth.invalid_token.

402 application/json Error

billing.payment_required: the account's plan is paid for through Stripe and its payment is not in good standing (params.status: past_due, unpaid, paused or canceled), so new mailboxes, domains and outgoing mail are on hold. Nothing was created or sent. Existing mail is kept, mail keeps arriving, and mailboxes and domains can still be read and deleted. The account's owner resolves it.

403 application/json Error

The credential lacks messages:read or messages:send (auth.insufficient_scope), the mailbox is suspended (mailboxes.suspended), sending is suspended for its account, domain, itself or the credential (messages.sending_suspended), its domain is not active (mailboxes.domain_inactive), its domain's sending records are wrong (domains.outbound_dns_invalid), or the plan's limits are reached: entitlements.email_limit, entitlements.storage_limit.

404 application/json Error

mailboxes.not_found: no such mailbox, or none this credential can reach. messages.not_found: no such message in this mailbox (or, for a credential without junk:read, a message in Junk).

409 application/json Error

idempotency.in_progress: a request with this Idempotency-Key is sending the message right now. Retry after Retry-After seconds. messages.in_junk: the message is in Junk, and nothing is sent from Junk; mark it as not spam first (markNotSpam).

413 application/json Error

request.too_large: the request body is over 24 MiB.

422 application/json Error

Invalid body or Idempotency-Key (request.invalid), the key already used for a different request (idempotency.key_reused), a Box recipient not accepting mail (messages.recipient_unavailable), or the message refused (messages.send_rejected, not sent). An address the reply would go to that Box cannot send to (messages.recipient_unsupported), no one to reply to (messages.no_recipients), too many addresses (messages.too_many_recipients), or a delivery report (messages.not_repliable).

429 application/json Error

auth.rate_limited with params.limit send.messages and params.scope mailbox: the mailbox's allowance of messages sent (60 per hour, up to 20 at once) is used up for now; or with params.limit send.recipients and params.scope account: the account's allowance of recipients, each in to, cc and bcc (200 per hour, up to 100 at once), is used up for now. The mail service may also refuse it with params.limit naming one of its recipient limits, each recipient one unit: send.domain.recipients (200 per hour, up to 100 at once), send.recipients.daily (1000 per 24 hours, up to 1000 at once), send.domain.recipients.daily (500 per 24 hours, up to 500 at once), params.scope domain or account. Both are shared by this API and the MCP server and by all the account's credentials, and a retry of a message already sent or being sent is never refused for them. Nothing was sent or counted, and the Idempotency-Key is unused. Retry after Retry-After seconds (the longer wait when both are used up), not before. auth.rate_limited with empty params: too many of the account's requests are in progress at once. Nothing was done. Retry after Retry-After seconds (1).

500 application/json Error

Unexpected error: server.error.

503 application/json Error

Not sent: messages.send_unavailable (with params.recipients and params.status when the mail system refused every recipient for now), domains.outbound_dns_unconfirmed (the domain's sending records could not be confirmed for 7 days). Maybe sent: messages.send_unconfirmed, storage.unavailable. Retry with the same Idempotency-Key.

Restore a message from the trash

POST /v1/mailboxes/{mailbox_id}/messages/{message_id}/restore restoreMessage

messages:delete

Requires messages:delete.

Restores a message from the trash: moves it back out of the trash, as its only folder, and returns it as it is now. Received mail goes to the Inbox, mail the mailbox sent to Sent. It keeps its id, content, attachments and read state, and it is no longer deleted automatically.

A message held in Junk (junk set, only for a credential with junk:read) goes back to Junk, still held: restoring never releases it, only markNotSpam does. It gets no new 30 days in Junk: it may be deleted automatically when it would have been in the trash (junk.expires_at; or, junk.retention unknown, at any time). Release it with markNotSpam to keep it.

Only a message in the trash is restored; any other is 409 messages.not_in_trash and nothing changes, so a restore repeated after it succeeded answers 409. A message deleted automatically or permanently is gone: 404 messages.not_found, and nothing is restored. 503 messages.move_unconfirmed: moving it could not be confirmed, and it may or may not have moved; read it to see where it is. 503 storage.unavailable: the stored mail could not be reached, and nothing changed; retry.

Requires messages:delete (and, for a message in Junk, junk:read). A credential restricted to one mailbox restores only in that mailbox.

Parameters

NameInTypeDescription
mailbox_id requiredpathstring
message_id requiredpathstring

Responses

200 application/json RestoredMessage

Successful Response

401 application/json Error

No usable credential: auth.missing_token or auth.invalid_token.

403 application/json Error

The credential lacks messages:delete (auth.insufficient_scope): messages:write does not allow deleting.

404 application/json Error

mailboxes.not_found: no such mailbox, or none this credential can reach. messages.not_found: no such message in this mailbox (or, for a credential without junk:read, a message in Junk).

409 application/json Error

messages.not_in_trash: the message is not in the trash; nothing changed. messages.operation_in_progress: another deletion in this mailbox is in progress; retry after Retry-After seconds (5).

422 application/json Error

Invalid parameters: request.invalid.

429 application/json Error

auth.rate_limited with params.limit write.mail and params.scope account: the account's write.mail allowance (120 requests per minute, up to 60 at once), shared by this API and the MCP server and by all the account's credentials, is used up for now. Nothing was done. Retry after Retry-After seconds, not before. auth.rate_limited with empty params: too many of the account's requests are in progress at once. Nothing was done. Retry after Retry-After seconds (1).

500 application/json Error

Unexpected error: server.error.

503 application/json Error

messages.move_unconfirmed: the move could not be confirmed (see the description); storage.unavailable: the stored mail could not be reached, and nothing changed. Retry after Retry-After seconds.

Spam: move a message from the Inbox to Junk

POST /v1/mailboxes/{mailbox_id}/messages/{message_id}/spam markSpam

junk:read junk:write messages:delete

Requires junk:read and junk:write and messages:delete.

Marks a message as spam: moves it from the Inbox to Junk, as its only folder, and returns it as listJunkMessages shows it (junk.reasons user). From then on it is held in Junk: it is no longer listed or searched with the mailbox's other mail, and it does not exist (404) for any credential without junk:read. Mark it as not spam (markNotSpam) to undo it.

It is a delayed deletion: mail in Junk is deleted automatically about 30 days after it went in (junk.expires_at), and cannot be recovered after that. An application marking mail as spam for a person should ask that person first. Nothing else learns from it: it does not train the spam filter.

Only mail received in the Inbox, and in no other folder, is marked. A message in the trash is 409 messages.in_trash (restore it first, restoreMessage); mail the mailbox sent, or a message in another folder or in several is 409 messages.not_in_inbox. Nothing changes then.

Marking a message that is already marked succeeds and changes nothing, so repeating the request is safe. 503 messages.move_unconfirmed: the message is already held in Junk (hidden as above), but its move could not be confirmed; send the same request again to finish. 503 storage.unavailable: the stored mail could not be reached, and nothing changed; retry.

Requires junk:read, junk:write and messages:delete, which an owner gives a credential only by choosing them.

Parameters

NameInTypeDescription
mailbox_id requiredpathstring
message_id requiredpathstring

Responses

200 application/json JunkMessage

Successful Response

401 application/json Error

No usable credential: auth.missing_token or auth.invalid_token.

403 application/json Error

The credential lacks the operation's scopes (auth.insufficient_scope): marking as spam needs junk:read, junk:write and messages:delete, which an owner gives only by choosing them.

404 application/json Error

mailboxes.not_found: no such mailbox, or none this credential can reach. messages.not_found: no such message in this mailbox (or, for a credential without junk:read, a message in Junk).

409 application/json Error

messages.in_trash: the message is in the trash; restore it first. messages.not_in_inbox: it is not mail received in the Inbox only. Nothing changed. messages.operation_in_progress: another deletion in this mailbox is in progress; retry after Retry-After seconds (5).

422 application/json Error

Invalid parameters: request.invalid.

429 application/json Error

auth.rate_limited with params.limit write.mail and params.scope account: the account's write.mail allowance (120 requests per minute, up to 60 at once), shared by this API and the MCP server and by all the account's credentials, is used up for now. Nothing was done. Retry after Retry-After seconds, not before. auth.rate_limited with empty params: too many of the account's requests are in progress at once. Nothing was done. Retry after Retry-After seconds (1).

500 application/json Error

Unexpected error: server.error.

503 application/json Error

messages.move_unconfirmed: the move could not be confirmed (see the description); storage.unavailable: the stored mail could not be reached, and nothing changed. Retry after Retry-After seconds.

Search messages

GET /v1/mailboxes/{mailbox_id}/search searchMessages

messages:read

Requires messages:read.

The mailbox's messages that match query, newest first: by received_at, then by id.

  • Box looks for the words of query in each message's sender, recipients, subject and text. Matching ignores case.
  • The result is the newest matches, at most limit of them, and it has no further pages. To find older mail, make the query more specific. To walk the whole mailbox, list it (listMessages).
  • Each result has the same form, and the same id, as in listMessages. Mail is found once Box has recorded its arrival, as its message.received event is.
  • Mail in Junk is never searched, whatever the credential.

query is 1 to 200 characters. limit defaults to 20 and may be at most 50. Requires messages:read.

Parameters

NameInTypeDescription
mailbox_id requiredpathstring
query requiredquerystring

The words to look for.

min length 1 · max length 200

limitqueryinteger

Most messages to return.

min 1 · max 50 · default 20

Responses

200 application/json MessageSearchResults

Successful Response

401 application/json Error

No usable credential: auth.missing_token or auth.invalid_token.

403 application/json Error

The credential lacks the operation's scope: auth.insufficient_scope.

404 application/json Error

mailboxes.not_found: no such mailbox, or none this credential can reach.

422 application/json Error

Invalid parameters: request.invalid.

429 application/json Error

auth.rate_limited with params.limit read.search and params.scope account: the account's read.search allowance (20 requests per minute, up to 10 at once), shared by this API and the MCP server and by all the account's credentials, is used up for now. Nothing was done. Retry after Retry-After seconds, not before. auth.rate_limited with empty params: too many of the account's requests are in progress at once. Nothing was done. Retry after Retry-After seconds (1).

500 application/json Error

Unexpected error: server.error.

503 application/json Error

Temporarily unavailable, retry later: storage.unavailable.

List threads

GET /v1/mailboxes/{mailbox_id}/threads listThreads

messages:read

Requires messages:read.

The mailbox's threads: each a conversation, the messages that answer one another (a message, the replies to it, the replies to those), the mail the mailbox sent included. Box threads mail by how its messages refer to each other (their Message-ID, In-Reply-To and References), among messages with the same subject once Re:, Fwd: and the like are left off: a reply whose subject was changed starts a thread of its own.

Threads are listed by their newest message, newest first, and each has the messages it shows in this listing: their ids oldest first (message_ids), their count, how many are unread, their senders, and the newest of them (latest, as listMessages shows a message).

  • Without folder: every thread with a message outside Junk and the trash, with those.
  • With folder (inbox, sent, drafts, archive, other): the threads with a message in that folder now, each with its messages outside Junk and the trash (an Inbox thread shows the replies the mailbox sent). With trash: the threads with a message in the trash, with those.
  • Mail in Junk is never part of a thread here, whatever the credential: never listed, counted or named.

Paging works as for listMessages: omit cursor for the first page, pass each next_cursor back as cursor (null on the last page). A listing is the threads as they were when its first page was requested; mail arriving later is in the next listing. If two threads become one between two pages (a message arrives that answers both), the one that became part of the other is not listed again, and the other may be listed a second time: keep the last one listed for each id. A page can hold fewer than limit threads, even none, with a next_cursor: follow it. Each folder's cursors are its own (422 pagination.invalid_cursor in another listing).

limit defaults to 20 and may be at most 100. Requires messages:read.

Parameters

NameInTypeDescription
mailbox_id requiredpathstring
cursorquerystring or null

A next_cursor from the previous page. Omit for the first page.

max length 1024

limitqueryinteger

Most threads to return.

min 1 · max 100 · default 20

folderquerystring: inbox, sent, drafts, trash, archive, other or null

Only the threads with a message in this folder now. Omit for every thread (but Junk).

Responses

200 application/json ThreadPage

Successful Response

401 application/json Error

No usable credential: auth.missing_token or auth.invalid_token.

403 application/json Error

The credential lacks the operation's scope: auth.insufficient_scope.

404 application/json Error

mailboxes.not_found: no such mailbox, or none this credential can reach.

422 application/json Error

Invalid parameters (request.invalid) or an unusable cursor (pagination.invalid_cursor).

429 application/json Error

auth.rate_limited with params.limit read.mail and params.scope account: the account's read.mail allowance (120 requests per minute, up to 60 at once), shared by this API and the MCP server and by all the account's credentials, is used up for now. Nothing was done. Retry after Retry-After seconds, not before. auth.rate_limited with empty params: too many of the account's requests are in progress at once. Nothing was done. Retry after Retry-After seconds (1).

500 application/json Error

Unexpected error: server.error.

503 application/json Error

Temporarily unavailable, retry later: storage.unavailable.

Get a thread

GET /v1/mailboxes/{mailbox_id}/threads/{thread_id} getThread

messages:read

Requires messages:read.

One thread's messages, oldest first, each as getMessage reads it (with its delivery state, for mail the mailbox sent).

  • Without folder: its messages outside Junk and the trash. With folder trash: its messages in the trash.
  • At most 100 messages, the newest ones; omitted says how many older ones are left out (read them with getMessage).
  • Mail in Junk is never part of a thread here, whatever the credential.
  • A thread id stays usable: when a thread becomes part of another (a message arrived that answers both), its id reads the thread it became, under that thread's id.
  • A thread with nothing to show (every message in Junk, deleted, or, without folder, in the trash) is 404 threads.not_found, as is another mailbox's thread.

Requires messages:read.

Parameters

NameInTypeDescription
mailbox_id requiredpathstring
thread_id requiredpathstring
folderquery"trash" or null

trash: its messages in the trash. Omit for the others (but Junk).

Responses

200 application/json Thread

Successful Response

401 application/json Error

No usable credential: auth.missing_token or auth.invalid_token.

403 application/json Error

The credential lacks the operation's scope: auth.insufficient_scope.

404 application/json Error

mailboxes.not_found: no such mailbox, or none this credential can reach. threads.not_found: no such thread in this mailbox, or none with a message to show.

422 application/json Error

Invalid parameters: request.invalid.

429 application/json Error

auth.rate_limited with params.limit read.mail and params.scope account: the account's read.mail allowance (120 requests per minute, up to 60 at once), shared by this API and the MCP server and by all the account's credentials, is used up for now. Nothing was done. Retry after Retry-After seconds, not before. auth.rate_limited with empty params: too many of the account's requests are in progress at once. Nothing was done. Retry after Retry-After seconds (1).

500 application/json Error

Unexpected error: server.error.

503 application/json Error

Temporarily unavailable, retry later: storage.unavailable.

Empty the trash

POST /v1/mailboxes/{mailbox_id}/trash/empty emptyTrash

messages:destroy

Requires messages:destroy.

Permanently deletes every message in the mailbox's trash. They cannot be recovered, the storage they used is free (getUsage shows that once the mailbox is measured, normally at once), and their ids are not found from then on.

What is in the trash is what the mailbox holds at the moment of the request, including mail not listed yet. A message in the trash that is also in another folder is left as it is and counted in skipped: deleting it (deleteMessage) leaves it in the trash only.

For a credential without junk:read, only what it can see is in the trash: a message held in Junk (deleted after it was held), one also filed in Junk, and one the mailbox has not checked yet are left as they are, and counted in neither destroyed nor skipped (nor params). With junk:read, they are permanently deleted like the rest, as long as they are in the trash only.

The answer counts the messages permanently deleted now (destroyed); an empty trash answers 0. They are deleted in batches, and when some could not be deleted now the answer is 503 messages.trash_not_emptied: params.destroyed were deleted for good, params.remaining were not, and params.skipped is as above. Empty the trash again after Retry-After seconds to delete the rest: repeating is always safe. 503 storage.unavailable: the trash could not be read, and nothing was deleted.

The deleted messages' events stay on the event feed, received messages' from and subject removed (data.redacted: true); sent messages are still delivered, and their outcomes reported.

Requires messages:destroy: messages:delete does not allow it. An application emptying the trash for a person should ask that person first; the credential's scope is standing authorization, not that person's approval.

Parameters

NameInTypeDescription
mailbox_id requiredpathstring

Responses

200 application/json EmptiedTrash

Successful Response

401 application/json Error

No usable credential: auth.missing_token or auth.invalid_token.

403 application/json Error

The credential lacks messages:destroy (auth.insufficient_scope): messages:delete (moving to the trash) does not allow permanent deletion.

404 application/json Error

mailboxes.not_found: no such mailbox, or none this credential can reach.

409 application/json Error

messages.operation_in_progress: another deletion in this mailbox is in progress; retry after Retry-After seconds (5).

422 application/json Error

Invalid parameters: request.invalid.

429 application/json Error

auth.rate_limited with params.limit write.trash and params.scope account: the account's write.trash allowance (10 requests per hour, up to 5 at once), shared by this API and the MCP server and by all the account's credentials, is used up for now. Nothing was done. Retry after Retry-After seconds, not before. auth.rate_limited with empty params: too many of the account's requests are in progress at once. Nothing was done. Retry after Retry-After seconds (1).

500 application/json Error

Unexpected error: server.error.

503 application/json Error

messages.trash_not_emptied: some messages could not be deleted now (params.destroyed, deleted for good; params.remaining, not deleted; params.skipped); empty the trash again after Retry-After seconds (30). storage.unavailable: the trash could not be read; nothing was deleted.

Drafts

List drafts

GET /v1/mailboxes/{mailbox_id}/drafts listDrafts

drafts:read

Requires drafts:read.

The mailbox's drafts: messages being written, by a person or an application, that have not been sent. Every client of the account sees the same drafts: a draft an application prepares appears in the webmail, and a draft written in the webmail can be read and changed here.

  • Most recently saved first, at most limit per page (100 at most); pass next_cursor back as cursor for the next page.
  • With thread_id: the replies drafted in that conversation.
  • A sent or discarded draft is not listed. Drafts are never part of the mailbox's messages or conversations (listMessages, listThreads, searchMessages).
  • Each draft has its text's start (preview), never the whole text: read a draft with getDraft.

Requires drafts:read.

Parameters

NameInTypeDescription
mailbox_id requiredpathstring
cursorquerystring or null

A next_cursor from the previous page. Omit for the first page.

max length 1024

limitqueryinteger

Most drafts to return.

min 1 · max 100 · default 50

thread_idquerystring or null

Only the replies drafted in this conversation (thr_...).

max length 64

Responses

200 application/json DraftPage

Successful Response

401 application/json Error

No usable credential: auth.missing_token or auth.invalid_token.

403 application/json Error

The credential lacks the operation's scopes (auth.insufficient_scope): drafts need drafts:read (and, to change them, drafts:write), which an owner gives only by choosing them; a reply's or forward's draft also messages:read.

404 application/json Error

mailboxes.not_found: no such mailbox, or none this credential can reach.

422 application/json Error

Invalid parameters (request.invalid) or an unusable cursor (pagination.invalid_cursor).

429 application/json Error

auth.rate_limited with params.limit read.mail and params.scope account: the account's read.mail allowance (120 requests per minute, up to 60 at once), shared by this API and the MCP server and by all the account's credentials, is used up for now. Nothing was done. Retry after Retry-After seconds, not before. auth.rate_limited with empty params: too many of the account's requests are in progress at once. Nothing was done. Retry after Retry-After seconds (1).

500 application/json Error

Unexpected error: server.error.

503 application/json Error

Temporarily unavailable, retry later: storage.unavailable.

Create a draft

POST /v1/mailboxes/{mailbox_id}/drafts createDraft

drafts:read drafts:write Idempotency-Key required

Requires drafts:read and drafts:write.

Creates a draft, at version 1: a new message (kind new, the default) or a reply, a reply to all or a forward of one of the mailbox's messages (message_id). Nothing is sent: sending a draft is a separate operation, which needs messages:send.

  • A draft may be incomplete: no recipient or subject yet.
  • For a reply, to, cc and subject left out are Box's (worked out when it is sent); for a forward, subject left out is Box's. Threading headers are always Box's.
  • Files are sent with their content (content_base64), at most 10 and 10 MiB in all.
  • Idempotency-Key: the same key and request again return the same draft (as it is now); the same key with another request is 422 idempotency.key_reused. Keys of drafts are their own: a key used for a send is another key here.
  • At most 500 open drafts per mailbox (403 drafts.limit_reached). Drafts count toward the mailbox's storage (403 entitlements.storage_limit when it is full; nothing is saved).
  • A draft of a suspended mailbox, or of an account whose payment needs attention, can still be saved: nothing leaves the mailbox until it is sent, and sending checks those.

Requires drafts:read and drafts:write; a reply or a forward also messages:read. A message in Junk is not found without junk:read, and with it 409 messages.in_junk: nothing is drafted from Junk.

Parameters

NameInTypeDescription
mailbox_id requiredpathstring
Idempotency-Key requiredheaderstring

Identifies this draft's creation across retries: 1 to 200 visible ASCII characters, unique per draft you create.

min length 1 · max length 200 · pattern ^[\x21-\x7e]{1,200}$

Request body application/json required

NewDraft

FieldTypeDescription
attachmentsarray of KeptDraftAttachment or OutgoingAttachment

The draft's own files, in order, at most 10 and 10 MiB in all: each either a new file (filename, content_type, content_base64, optional charset) or {"id": "att_..."}, one the draft already has, kept as it is without sending its content again. A file of the draft left out is removed. A forward's message's own attachments are not here (original_attachment_ids).

max items 10

bccarray of string (email)

Blind copies: addresses the message will be sent to without any recipient seeing them. At most 50 in each field and 100 in all.

max items 50

ccarray of string (email)

The draft's Cc; as to (for a reply, [] is no Cc, leaving it out keeps Box's).

max items 50

include_original_attachmentsboolean

Only for a forward: whether the forwarded message's own attachments go with it when it is sent. false forwards its text only.

default true

kindstring: new, reply, reply_all, forward

What the draft is: a message of its own (new), or a reply, a reply_all or a forward of the message message_id. It never changes.

default "new"

message_idstring

For a reply, a reply to all or a forward: the message (msg_...) of this mailbox it answers or forwards. Reading it needs messages:read.

min length 1 · max length 64

original_attachment_idsarray of string

Only for a forward: which of the forwarded message's own attachments go, by the id its read lists each with (att_...), each once. [] sends none. Leave it out to send them all. Not with include_original_attachments: false.

max items 10

subjectstring

The subject. For a reply or a forward, leave it out to keep Box's (Re: or Fwd: and the message's); for a new message, leaving it out is "".

max length 998

textstring

The plain-text body, as it will be sent. For a forward, the note above the forwarded message, which Box adds when it is sent.

max length 1000000 · default ""

toarray of string (email)

The draft's To. A draft may be incomplete: no recipient yet is fine, and duplicates are checked when it is sent. For a reply, leave it out to keep Box's (who the reply would go to, worked out from the message when it is sent); for any other draft, leaving it out is [].

max items 50

Responses

201 application/json Draft

Successful Response

401 application/json Error

No usable credential: auth.missing_token or auth.invalid_token.

403 application/json Error

The credential lacks the operation's scopes (auth.insufficient_scope): drafts need drafts:read (and, to change them, drafts:write), which an owner gives only by choosing them; a reply's or forward's draft also messages:read. drafts.limit_reached: the mailbox has its most open drafts. entitlements.storage_limit: the mailbox's storage is full.

404 application/json Error

mailboxes.not_found: no such mailbox, or none this credential can reach. messages.not_found: no such message in this mailbox (or, for a credential without junk:read, a message in Junk). (a reply's or forward's message_id)

409 application/json Error

messages.in_junk: the message is in Junk; nothing is drafted from Junk. drafts.sent, drafts.discarded: the draft this Idempotency-Key created has ended.

413 application/json Error

request.too_large: the request body is over 24 MiB.

422 application/json Error

Invalid body or Idempotency-Key (request.invalid), the key already used for a different request (idempotency.key_reused), a file to keep the draft does not have (drafts.attachment_unknown, params.attachment_ids), or a forward's attachment its message does not list (messages.original_attachment_unknown).

429 application/json Error

auth.rate_limited with params.limit write.drafts and params.scope account: the account's write.drafts allowance (120 requests per minute, up to 60 at once), shared by this API and the MCP server and by all the account's credentials, is used up for now. Nothing was done. Retry after Retry-After seconds, not before. auth.rate_limited with empty params: too many of the account's requests are in progress at once. Nothing was done. Retry after Retry-After seconds (1).

500 application/json Error

Unexpected error: server.error.

503 application/json Error

Temporarily unavailable, retry later: storage.unavailable.

Get a draft

GET /v1/mailboxes/{mailbox_id}/drafts/{draft_id} getDraft

drafts:read

Requires drafts:read.

One draft as it is now: its current version, its recipients, subject, text and own attachments, what it is (kind, and a reply's or forward's message_id), and who saved it last (updated_by). Save your changes onto this version.

  • to, cc and subject are null where a reply leaves them to Box (who a reply goes to, and Re: ..., worked out from the message when it is sent).
  • A draft that was sent is 409 drafts.sent (with the sent message's id), one that was discarded 409 drafts.discarded, for 30 days; then, like a draft of another mailbox, 404 drafts.not_found.

Requires drafts:read. The content was written by whoever saved it (a person, or an application): review a draft another client saved before sending it.

Parameters

NameInTypeDescription
mailbox_id requiredpathstring
draft_id requiredpathstring

Responses

200 application/json Draft

Successful Response

401 application/json Error

No usable credential: auth.missing_token or auth.invalid_token.

403 application/json Error

The credential lacks the operation's scopes (auth.insufficient_scope): drafts need drafts:read (and, to change them, drafts:write), which an owner gives only by choosing them; a reply's or forward's draft also messages:read.

404 application/json Error

mailboxes.not_found: no such mailbox, or none this credential can reach. drafts.not_found: no such draft in this mailbox.

409 application/json Error

drafts.sent (params.message_id), drafts.discarded: the draft ended.

422 application/json Error

Invalid parameters: request.invalid.

429 application/json Error

auth.rate_limited with params.limit read.mail and params.scope account: the account's read.mail allowance (120 requests per minute, up to 60 at once), shared by this API and the MCP server and by all the account's credentials, is used up for now. Nothing was done. Retry after Retry-After seconds, not before. auth.rate_limited with empty params: too many of the account's requests are in progress at once. Nothing was done. Retry after Retry-After seconds (1).

500 application/json Error

Unexpected error: server.error.

503 application/json Error

Temporarily unavailable, retry later: storage.unavailable.

Save a draft

PUT /v1/mailboxes/{mailbox_id}/drafts/{draft_id} updateDraft

drafts:read drafts:write

Requires drafts:read and drafts:write.

Saves the draft's whole content anew, as the edit of version: the draft at its next version. Anything left out is its default (no recipients, an empty text, no files; a reply's to, cc and subject Box's), so send every field each time.

  • version must be the draft's current one. If another client saved it since, it is 409 drafts.version_conflict (with params.version, params.updated_at, params.updated_by) and nothing changes: read the draft, then save your changes onto its current version. Box never overwrites a version you did not see.
  • Keep one of the draft's files with {"id": "att_..."}: its content is not sent again, and its id stays the same. A file left out is removed.
  • A draft that is being sent (409 drafts.submitting), was sent (drafts.sent) or discarded (drafts.discarded) cannot be saved.
  • Its kind and message never change.

Requires drafts:read and drafts:write; a reply's or forward's draft also messages:read.

Parameters

NameInTypeDescription
mailbox_id requiredpathstring
draft_id requiredpathstring

Request body application/json required

DraftUpdate

FieldTypeDescription
attachmentsarray of KeptDraftAttachment or OutgoingAttachment

The draft's own files, in order, at most 10 and 10 MiB in all: each either a new file (filename, content_type, content_base64, optional charset) or {"id": "att_..."}, one the draft already has, kept as it is without sending its content again. A file of the draft left out is removed. A forward's message's own attachments are not here (original_attachment_ids).

max items 10

bccarray of string (email)

Blind copies: addresses the message will be sent to without any recipient seeing them. At most 50 in each field and 100 in all.

max items 50

ccarray of string (email)

The draft's Cc; as to (for a reply, [] is no Cc, leaving it out keeps Box's).

max items 50

include_original_attachmentsboolean

Only for a forward: whether the forwarded message's own attachments go with it when it is sent. false forwards its text only.

default true

original_attachment_idsarray of string

Only for a forward: which of the forwarded message's own attachments go, by the id its read lists each with (att_...), each once. [] sends none. Leave it out to send them all. Not with include_original_attachments: false.

max items 10

subjectstring

The subject. For a reply or a forward, leave it out to keep Box's (Re: or Fwd: and the message's); for a new message, leaving it out is "".

max length 998

textstring

The plain-text body, as it will be sent. For a forward, the note above the forwarded message, which Box adds when it is sent.

max length 1000000 · default ""

toarray of string (email)

The draft's To. A draft may be incomplete: no recipient yet is fine, and duplicates are checked when it is sent. For a reply, leave it out to keep Box's (who the reply would go to, worked out from the message when it is sent); for any other draft, leaving it out is [].

max items 50

version requiredinteger

The version this save edits: the version of the draft as you last read it. Another save since is 409 drafts.version_conflict, and nothing changes.

min 1

Responses

200 application/json Draft

Successful Response

401 application/json Error

No usable credential: auth.missing_token or auth.invalid_token.

403 application/json Error

The credential lacks the operation's scopes (auth.insufficient_scope): drafts need drafts:read (and, to change them, drafts:write), which an owner gives only by choosing them; a reply's or forward's draft also messages:read. drafts.limit_reached: the mailbox has its most open drafts. entitlements.storage_limit: the mailbox's storage is full.

404 application/json Error

mailboxes.not_found: no such mailbox, or none this credential can reach. drafts.not_found: no such draft in this mailbox.

409 application/json Error

drafts.version_conflict: the draft was saved since that version (params.version, params.updated_at, params.updated_by); nothing changed. drafts.submitting: it is being sent (Retry-After). drafts.sent (params.message_id), drafts.discarded: it ended.

413 application/json Error

request.too_large: the request body is over 24 MiB.

422 application/json Error

Invalid body or Idempotency-Key (request.invalid), the key already used for a different request (idempotency.key_reused), a file to keep the draft does not have (drafts.attachment_unknown, params.attachment_ids), or a forward's attachment its message does not list (messages.original_attachment_unknown).

429 application/json Error

auth.rate_limited with params.limit write.drafts and params.scope account: the account's write.drafts allowance (120 requests per minute, up to 60 at once), shared by this API and the MCP server and by all the account's credentials, is used up for now. Nothing was done. Retry after Retry-After seconds, not before. auth.rate_limited with empty params: too many of the account's requests are in progress at once. Nothing was done. Retry after Retry-After seconds (1).

500 application/json Error

Unexpected error: server.error.

503 application/json Error

Temporarily unavailable, retry later: storage.unavailable.

Discard a draft

DELETE /v1/mailboxes/{mailbox_id}/drafts/{draft_id} discardDraft

drafts:read drafts:write

Requires drafts:read and drafts:write.

Discards the draft for good, as of version: its content is deleted and cannot be recovered. Nothing is sent.

  • version must be the draft's current one: a version saved since is 409 drafts.version_conflict, and nothing is discarded.
  • Afterwards the draft is 409 drafts.discarded for 30 days, then not found.

Requires drafts:read and drafts:write. An application should discard a person's draft only at their request.

Parameters

NameInTypeDescription
mailbox_id requiredpathstring
draft_id requiredpathstring
version requiredqueryinteger

The draft's current version, as you last read it.

min 1

Responses

200 application/json DiscardedDraft

Successful Response

401 application/json Error

No usable credential: auth.missing_token or auth.invalid_token.

403 application/json Error

The credential lacks the operation's scopes (auth.insufficient_scope): drafts need drafts:read (and, to change them, drafts:write), which an owner gives only by choosing them; a reply's or forward's draft also messages:read.

404 application/json Error

mailboxes.not_found: no such mailbox, or none this credential can reach. drafts.not_found: no such draft in this mailbox.

409 application/json Error

drafts.version_conflict: the draft was saved since that version (params.version, params.updated_at, params.updated_by); nothing changed. drafts.submitting: it is being sent (Retry-After). drafts.sent (params.message_id), drafts.discarded: it ended.

422 application/json Error

Invalid parameters: request.invalid.

429 application/json Error

auth.rate_limited with params.limit write.drafts and params.scope account: the account's write.drafts allowance (120 requests per minute, up to 60 at once), shared by this API and the MCP server and by all the account's credentials, is used up for now. Nothing was done. Retry after Retry-After seconds, not before. auth.rate_limited with empty params: too many of the account's requests are in progress at once. Nothing was done. Retry after Retry-After seconds (1).

500 application/json Error

Unexpected error: server.error.

503 application/json Error

Temporarily unavailable, retry later: storage.unavailable.

Download a draft's attachment

GET /v1/mailboxes/{mailbox_id}/drafts/{draft_id}/attachments/{attachment_id} getDraftAttachment

drafts:read

Requires drafts:read.

The content of one of the draft's own files, by the id the draft lists it with, as getAttachment hands out a message's: the same headers, limits and attachment bytes. A forward's message's own attachments are read from the message (getAttachment).

Requires drafts:read.

Parameters

NameInTypeDescription
mailbox_id requiredpathstring
draft_id requiredpathstring
attachment_id requiredpathstring

Responses

200 application/octet-stream string (binary)

The attachment's content, as raw bytes.

401 application/json Error

No usable credential: auth.missing_token or auth.invalid_token.

403 application/json Error

The credential lacks the operation's scopes (auth.insufficient_scope): drafts need drafts:read (and, to change them, drafts:write), which an owner gives only by choosing them; a reply's or forward's draft also messages:read.

404 application/json Error

mailboxes.not_found: no such mailbox, or none this credential can reach. drafts.not_found: no such draft in this mailbox. attachments.not_found: the draft has no attachment with this id.

409 application/json Error

drafts.sent, drafts.discarded: the draft ended.

422 application/json Error

attachments.too_large: the attachment is larger than the limit (params.size, params.max_bytes).

429 application/json Error

auth.rate_limited with params.limit attachments and params.scope account: the account's attachments allowance (30 requests per minute, up to 15 at once), shared by this API and the MCP server and by all the account's credentials, is used up for now. Nothing was done. Retry after Retry-After seconds, not before. auth.rate_limited with params.limit attachment.bytes and params.scope account: the account's allowance of attachment bytes downloaded from storage (1 GiB per hour, up to 256 MiB at once), shared by downloading attachments and forwarding messages with theirs, through this API and the MCP server, is used up for now. Nothing was downloaded. Retry after Retry-After seconds, not before. auth.rate_limited with empty params: too many of the account's requests are in progress at once. Nothing was done. Retry after Retry-After seconds (1).

500 application/json Error

Unexpected error: server.error.

503 application/json Error

Temporarily unavailable, retry later: storage.unavailable.

Send a draft

POST /v1/mailboxes/{mailbox_id}/drafts/{draft_id}/send sendDraft

drafts:read messages:send

Requires drafts:read and messages:send.

Sends the draft as real email, as the version you name: the version you (or the person you act for) reviewed. Box composes the message from that version and sends it exactly as sendMessage, replyToMessage, replyAllToMessage or forwardMessage would (its kind): the same checks, limits, allowances, Bcc handling, delivery reporting and errors (see sendMessage). The draft itself is never sent as stored.

  • version must be the draft's current one. If it was saved since, it is 409 drafts.version_conflict (with params.version, params.updated_at, params.updated_by) and nothing is sent: read it again, and send the version that was reviewed.
  • A reply's or a reply to all's recipients left to Box, its subject and its threading (In-Reply-To, References) are worked out from its message now, as replyToMessage does; a changed subject starts a conversation of its own. A forward gets the forwarded message (its sender, date, subject, recipients, text and chosen attachments) now, after the draft's text and files, and is not part of that message's conversation. If that message no longer exists, it is 409 drafts.source_gone: nothing is sent, and the draft is unchanged.
  • A draft is checked as a send when it is sent, not when it is saved: nothing in To is 422 messages.no_recipients, an address in more than one field (or twice in one) 422 drafts.duplicate_recipients (params.recipients), more than 50 in a field or 100 in all 422 messages.too_many_recipients. Its files are sent with their names, types and character sets, at most 10 and 10 MiB with a forwarded message's; they count toward the account's attachment bytes, as downloading them does.

At most once. Box sends each version of a draft at most once, however often and however concurrently it is asked; no Idempotency-Key is needed. While it is being sent the draft is state: submitting and cannot be saved, discarded or sent as another version (409 drafts.submitting).

  • 202: sent. The draft is sent: from now on reading or saving it is 409 drafts.sent (with params.message_id, the sent message's id). Sending the same version again answers the same 202 and sends nothing, so a response lost on the way can be asked for again.
  • Not sent (403, 422, 429, messages.send_unavailable, a 503 that says so): the draft is open again at the same version, unchanged. Change it, or send it again.
  • 503 messages.send_unconfirmed: Box does not know yet whether it was sent. The draft stays submitting until Box finds out: send the same version again later to get the outcome; it is never sent twice. If Box never confirms it, the draft stays as it is: read it, and write a new one if the person decides to send it again (which may deliver it twice).
  • 409 drafts.submitting: another request is sending it now; ask again after Retry-After seconds for the outcome.

Requires drafts:read and messages:send (not drafts:write); a reply's or forward's draft also messages:read. drafts:write alone never sends. Send a draft only when the person asked to send it; it may have been written or changed by another application (updated_by).

Parameters

NameInTypeDescription
mailbox_id requiredpathstring
draft_id requiredpathstring

Request body application/json required

DraftSend

FieldTypeDescription
version requiredinteger

The version to send: the version of the draft as you (or the person you act for) last read and reviewed it. If it was saved since, it is 409 drafts.version_conflict and nothing is sent.

min 1

Responses

202 application/json SubmittedMessage

Successful Response

401 application/json Error

No usable credential: auth.missing_token or auth.invalid_token.

402 application/json Error

billing.payment_required: the account's plan is paid for through Stripe and its payment is not in good standing (params.status: past_due, unpaid, paused or canceled), so new mailboxes, domains and outgoing mail are on hold. Nothing was created or sent. Existing mail is kept, mail keeps arriving, and mailboxes and domains can still be read and deleted. The account's owner resolves it.

403 application/json Error

The credential lacks drafts:read or messages:send (or, for a reply's or forward's draft, messages:read) (auth.insufficient_scope), or the send is refused as sendMessage's is (mailboxes.suspended, messages.sending_suspended, mailboxes.domain_inactive, domains.outbound_dns_invalid, entitlements.email_limit, entitlements.storage_limit). Nothing was sent.

404 application/json Error

mailboxes.not_found: no such mailbox, or none this credential can reach. drafts.not_found: no such draft in this mailbox.

409 application/json Error

drafts.version_conflict: the draft was saved since that version (params.version, params.updated_at, params.updated_by); nothing changed. drafts.submitting: it is being sent (Retry-After). drafts.sent (params.message_id), drafts.discarded: it ended. drafts.source_gone: the message a reply's or forward's draft answers no longer exists; nothing was sent. messages.in_junk: it is in Junk.

413 application/json Error

request.too_large: the request body is too large.

422 application/json Error

Invalid body (request.invalid); the draft not sendable as it is (messages.no_recipients, drafts.duplicate_recipients, messages.too_many_recipients, messages.recipient_unsupported, messages.attachments_too_large, and a reply's or forward's other errors), a Box recipient not accepting mail (messages.recipient_unavailable), or the message refused (messages.send_rejected, not sent).

429 application/json Error

auth.rate_limited with params.limit send.messages and params.scope mailbox: the mailbox's allowance of messages sent (60 per hour, up to 20 at once) is used up for now; or with params.limit send.recipients and params.scope account: the account's allowance of recipients, each in to, cc and bcc (200 per hour, up to 100 at once), is used up for now. The mail service may also refuse it with params.limit naming one of its recipient limits, each recipient one unit: send.domain.recipients (200 per hour, up to 100 at once), send.recipients.daily (1000 per 24 hours, up to 1000 at once), send.domain.recipients.daily (500 per 24 hours, up to 500 at once), params.scope domain or account. Both are shared by this API and the MCP server and by all the account's credentials, and a retry of a message already sent or being sent is never refused for them. Nothing was sent or counted, and the Idempotency-Key is unused. Retry after Retry-After seconds (the longer wait when both are used up), not before. auth.rate_limited with params.limit attachment.bytes and params.scope account: the account's allowance of attachment bytes downloaded from storage (1 GiB per hour, up to 256 MiB at once), shared by downloading attachments and forwarding messages with theirs, through this API and the MCP server, is used up for now. Nothing was downloaded or sent, and the Idempotency-Key is unused. Retry after Retry-After seconds, not before. auth.rate_limited with empty params: too many of the account's requests are in progress at once. Nothing was done. Retry after Retry-After seconds (1).

500 application/json Error

Unexpected error: server.error.

503 application/json Error

Not sent (the draft is open again): messages.send_unavailable (with params.recipients and params.status when the mail system refused every recipient for now), domains.outbound_dns_unconfirmed. Maybe sent (the draft stays submitting): messages.send_unconfirmed, and storage.unavailable, which may be either. Send the same version again for the outcome: it is never sent twice.

Events

Poll the event feed

GET /v1/events listEvents

events:read Account-wide: not with a mailbox restriction

Requires events:read, and a credential without a mailbox restriction.

The customer's events, across all of its mailboxes, oldest first.

  • Omit after to start from the first event. Then pass each response's next_cursor as after to continue after the last event it returned.
  • The order is stable and complete: new events are only ever added after the ones already visible, so continuing from next_cursor never skips one.
  • An empty data returns next_cursor equal to after: poll again later with the same value. has_more means more events can be fetched right away.
  • Requests are safe to repeat. The same after returns the same events, plus any added since. If you retry without having saved next_cursor, you receive events again; use the event id to skip duplicates.
  • Cursors are opaque and belong to one customer. Do not build or modify them. An unusable after fails with 422 events.invalid_cursor.
  • Events are currently kept indefinitely. If a retention period is introduced, a cursor older than it will fail with 410 events.cursor_expired instead of silently skipping events.

The same events can also be delivered to a URL of yours as they appear (createWebhook). The feed stays the complete record: use it to catch up on anything a webhook did not deliver.

Mail put in Junk is not announced when it arrives: its message.received appears only if it is marked as not spam (markNotSpam), with data.released_from_junk: true.

limit defaults to 100 and may be at most 500. Requires events:read and a credential without a mailbox restriction.

Parameters

NameInTypeDescription
afterquerystring or null

A next_cursor from an earlier response. Omit to start from the first event.

limitqueryinteger

Most events to return.

min 1 · max 500 · default 100

Responses

200 application/json EventPage

Successful Response

401 application/json Error

No usable credential: auth.missing_token or auth.invalid_token.

403 application/json Error

The credential lacks events:read (auth.insufficient_scope) or is restricted to some mailboxes (auth.mailbox_restricted).

422 application/json Error

Invalid parameters (request.invalid) or an unusable after (events.invalid_cursor).

429 application/json Error

auth.rate_limited with params.limit read.light and params.scope account: the account's read.light allowance (300 requests per minute, up to 120 at once), shared by this API and the MCP server and by all the account's credentials, is used up for now. Nothing was done. Retry after Retry-After seconds, not before.

500 application/json Error

Unexpected error: server.error.

503 application/json Error

Temporarily unavailable, retry later: storage.unavailable.

Webhooks

List webhooks

GET /v1/webhooks listWebhooks

webhooks:read Account-wide: not with a mailbox restriction

Requires webhooks:read, and a credential without a mailbox restriction.

The customer's webhooks, oldest first. Their secrets are not shown. Requires webhooks:read and a credential without a mailbox restriction.

Responses

200 application/json WebhookList

Successful Response

401 application/json Error

No usable credential: auth.missing_token or auth.invalid_token.

403 application/json Error

The credential lacks the operation's scopes (auth.insufficient_scope) or is restricted to some mailboxes (auth.mailbox_restricted).

422 application/json Error

Invalid parameters: request.invalid.

429 application/json Error

auth.rate_limited with params.limit read.light and params.scope account: the account's read.light allowance (300 requests per minute, up to 120 at once), shared by this API and the MCP server and by all the account's credentials, is used up for now. Nothing was done. Retry after Retry-After seconds, not before.

500 application/json Error

Unexpected error: server.error.

503 application/json Error

Temporarily unavailable, retry later: storage.unavailable.

Create a webhook

POST /v1/webhooks createWebhook

webhooks:write events:read Account-wide: not with a mailbox restriction

Requires webhooks:write and events:read, and a credential without a mailbox restriction.

Creates a webhook: Box delivers the customer's events (the ones GET /v1/events returns, from every mailbox) to url as they appear, one HTTPS POST per event.

Which events. A new webhook delivers the events that appear on the feed after it was created. Events already on the feed are not delivered: read them from GET /v1/events. event_cursor marks the boundary exactly: GET /v1/events?after=<event_cursor> lists the events the webhook has still to deliver.

The request. The body is the event, exactly as GET /v1/events returns it (id, type, mailbox_id, message_id, created_at, data), as JSON. Every event type is delivered, including types added later; skip the ones you do not handle. Headers:

  • Box-Webhook-Id: the webhook's id;
  • Box-Event-Id: the event's id;
  • Box-Timestamp: when this attempt was signed, in seconds since 1970-01-01 UTC;
  • Box-Signature: v1= followed by the signature, 64 lower-case hex digits.

Verifying it. The signature is the HMAC-SHA256 of the timestamp, a dot (.), and the raw request body, keyed with the webhook's secret (its UTF-8 bytes), in hex. To check a request:

  1. read the raw body, before parsing it;
  2. check that Box-Timestamp is recent (within 5 minutes, say);
  3. compute HMAC-SHA256(secret, Box-Timestamp + "." + raw body) in hex;
  4. compare it with the value after v1= in constant time;
  5. skip the event if you have already handled its id.

Delivery. Answer with any 2xx status to accept an event. Anything else (another status, a redirect, a connection or TLS failure, no answer within 15 seconds) fails the attempt, and Box sends the same event again later: after 10 seconds, then twice as long after each further failure, at most an hour apart. Redirects are not followed, and what the response contains is ignored. Events are delivered one at a time, in the feed's order: the next one is sent only once the previous one is accepted, so later events wait behind one that keeps failing. An event can be delivered more than once (after a failed attempt that your server did in fact receive, for example): use its id to discard duplicates. If attempts keep failing for 3 days, Box stops and the webhook's state becomes failed; nothing is lost from the feed.

The URL. It must be https, on port 443 or 1024-65535, without user name or password or fragment, and its host must be a public DNS name or address: not a private, loopback, link-local, multicast or other special-purpose address, nor a name that resolves to one. Box checks this when the webhook is created and again, on the address it connects to, for every delivery. A URL that is refused fails with 422 webhooks.invalid_destination, with params.reason one of https_required, credentials_not_allowed, port_not_allowed, invalid_host, address_not_allowed, invalid_url.

The response's secret is shown only once. A customer can have at most 10 webhooks (403 webhooks.limit_reached). To change a webhook's URL or secret, create a new webhook, then delete the old one: meanwhile, events arrive at both, each signed with its own webhook's secret.

Requires webhooks:write and events:read, and a credential without a mailbox restriction.

Request body application/json required

NewWebhook

FieldTypeDescription
url requiredstring

Where to deliver events: an https URL on port 443 or 1024-65535, without user name or password, whose host is a public DNS name or address. See createWebhook.

max length 2048

Responses

201 application/json CreatedWebhook

Successful Response

401 application/json Error

No usable credential: auth.missing_token or auth.invalid_token.

403 application/json Error

The credential lacks the operation's scopes (auth.insufficient_scope) or is restricted to some mailboxes (auth.mailbox_restricted), or the customer has as many webhooks as it may (webhooks.limit_reached).

413 application/json Error

request.too_large: the request body is too large.

422 application/json Error

Invalid body (request.invalid), or a URL Box does not deliver to (webhooks.invalid_destination, with params.reason).

429 application/json Error

auth.rate_limited with params.limit account.change and params.scope account: the account's account.change allowance (30 requests per hour, up to 10 at once), shared by this API and the MCP server and by all the account's credentials, is used up for now. Nothing was done. Retry after Retry-After seconds, not before.

500 application/json Error

Unexpected error: server.error.

503 application/json Error

Temporarily unavailable, retry later: storage.unavailable.

Get a webhook

GET /v1/webhooks/{webhook_id} getWebhook

webhooks:read Account-wide: not with a mailbox restriction

Requires webhooks:read, and a credential without a mailbox restriction.

One webhook, without its secret. Requires webhooks:read and a credential without a mailbox restriction.

Parameters

NameInTypeDescription
webhook_id requiredpathstring

Responses

200 application/json Webhook

Successful Response

401 application/json Error

No usable credential: auth.missing_token or auth.invalid_token.

403 application/json Error

The credential lacks the operation's scopes (auth.insufficient_scope) or is restricted to some mailboxes (auth.mailbox_restricted).

404 application/json Error

webhooks.not_found: no such webhook.

422 application/json Error

Invalid parameters: request.invalid.

429 application/json Error

auth.rate_limited with params.limit read.light and params.scope account: the account's read.light allowance (300 requests per minute, up to 120 at once), shared by this API and the MCP server and by all the account's credentials, is used up for now. Nothing was done. Retry after Retry-After seconds, not before.

500 application/json Error

Unexpected error: server.error.

503 application/json Error

Temporarily unavailable, retry later: storage.unavailable.

Delete a webhook

DELETE /v1/webhooks/{webhook_id} deleteWebhook

webhooks:write Account-wide: not with a mailbox restriction

Requires webhooks:write, and a credential without a mailbox restriction.

Deletes the webhook: no delivery to it starts afterwards (one already under way may still arrive). The events stay on GET /v1/events. Requires webhooks:write and a credential without a mailbox restriction.

Parameters

NameInTypeDescription
webhook_id requiredpathstring

Responses

204

Successful Response

401 application/json Error

No usable credential: auth.missing_token or auth.invalid_token.

403 application/json Error

The credential lacks the operation's scopes (auth.insufficient_scope) or is restricted to some mailboxes (auth.mailbox_restricted).

404 application/json Error

webhooks.not_found: no such webhook.

422 application/json Error

Invalid parameters: request.invalid.

429 application/json Error

auth.rate_limited with params.limit account.change and params.scope account: the account's account.change allowance (30 requests per hour, up to 10 at once), shared by this API and the MCP server and by all the account's credentials, is used up for now. Nothing was done. Retry after Retry-After seconds, not before.

500 application/json Error

Unexpected error: server.error.

503 application/json Error

Temporarily unavailable, retry later: storage.unavailable.

Usage

Check whether the account fits a plan

GET /v1/plan-eligibility getPlanEligibility

usage:read Account-wide: not with a mailbox restriction

Requires usage:read, and a credential without a mailbox restriction.

Whether the account fits target_plan now: each resource against the target's limit, what blocks the move, and the answer. This changes nothing: no plan is changed by it, or by anything else in this API.

Storage measured now. For a move to a plan with less storage (from starter, the Plus plan, to free), Box first measures the mail stored in every active mailbox of the account, and answers on that figure (storage.fresh). The measurement is kept: getUsage shows it afterwards. A move to a larger plan, or to the account's own, measures nothing, and storage is null.

The answer is 200 whether the account fits or not:

  • eligible: true and blockers empty: everything fits.
  • eligible: false: blockers names each resource over the target's limit (mailboxes, domains, storage_bytes), with used, limit and excess. Delete what is over, then check again.

Mailboxes and domains are counted as their limits are enforced; the monthly email count never blocks a move. storage.fresh is false only beside a mailbox or domain blocker: storage was then not needed for the answer, is shown as last measured, and is never a blocker. A move is checked again when it is made, since usage can change in between.

When storage cannot be measured, the answer is 503 entitlements.usage_unavailable, not a blocker: whether the account fits is not known. params.reason says why: usage_unreadable (a mailbox's storage could not be read), mailbox_busy (a mailbox stayed busy throughout the check), mailbox_provisioning (a mailbox was being created throughout the check) or mailboxes_changing (mailboxes kept being created during the check); params.mailbox_count is how many mailboxes were not measured. Retry after Retry-After seconds.

Limits. A check measures every mailbox, so an account may check 5 times every 5 minutes, through this API and the MCP server together (429 auth.rate_limited, with Retry-After). Reading usage (getUsage) does not count. Check when you need to know whether the account can move, not in a loop.

target_plan is free or starter (the Plus plan; 422 request.invalid otherwise). Requires usage:read and a credential without a mailbox restriction.

Parameters

NameInTypeDescription
target_plan requiredquerystring: free, starter

The plan to check the account against: free or starter (the Plus plan).

Responses

200 application/json PlanEligibility

Successful Response

401 application/json Error

No usable credential: auth.missing_token or auth.invalid_token.

403 application/json Error

The credential lacks usage:read (auth.insufficient_scope) or is restricted to one mailbox (auth.mailbox_restricted): usage covers the whole account.

422 application/json Error

request.invalid: target_plan is missing, or not free or starter (the Plus plan).

429 application/json Error

auth.rate_limited: the account has made its 5 plan eligibility checks for these 5 minutes, through this API and the MCP server together. Retry after Retry-After seconds. Or, with empty params, too many of the account's requests are in progress at once; nothing was measured. Retry after Retry-After seconds (1).

500 application/json Error

Unexpected error: server.error.

503 application/json Error

entitlements.usage_unavailable: the account's storage could not be measured now (params.reason, params.mailbox_count), so whether it fits is not known; nothing was changed. Retry after Retry-After seconds. storage.unavailable.

Get the account's usage of its plan

GET /v1/usage getUsage

usage:read Account-wide: not with a mailbox restriction

Requires usage:read, and a credential without a mailbox restriction.

The account's plan, each of its limits, and what the account uses of them, as Box has recorded it.

  • mailboxes and domains are counted exactly as their limits are enforced when one is created (403 entitlements.mailbox_limit, 403 entitlements.domain_limit).
  • emails counts the recipients of the mail sent in the current calendar month (UTC), as the monthly email limit does (403 entitlements.email_limit): every address in to, cc and bcc, except those the mail system refused for now when the message was submitted (refused_temporarily). It starts again from 0 at reset_at.
  • storage is the mail stored in all of the account's mailboxes, in bytes, as last measured (403 entitlements.storage_limit when it is at the limit). Box measures a mailbox again after its mail changes, and regularly besides; measured_at says how current the total is (the oldest of the mailboxes' latest measurements). To know the storage now, check plan eligibility (getPlanEligibility), which measures it.
  • used is always the real figure and can be more than limit, for example after a move to a smaller plan; remaining is then 0, never negative.

Reading usage changes nothing and measures nothing, so it can be called whenever it helps; it counts toward the account's light reads only. Requires usage:read and a credential without a mailbox restriction: usage covers the whole account.

Responses

200 application/json AccountUsage

Successful Response

401 application/json Error

No usable credential: auth.missing_token or auth.invalid_token.

403 application/json Error

The credential lacks usage:read (auth.insufficient_scope) or is restricted to one mailbox (auth.mailbox_restricted): usage covers the whole account.

422 application/json Error

Invalid parameters: request.invalid.

429 application/json Error

auth.rate_limited with params.limit read.light and params.scope account: the account's read.light allowance (300 requests per minute, up to 120 at once), shared by this API and the MCP server and by all the account's credentials, is used up for now. Nothing was done. Retry after Retry-After seconds, not before.

500 application/json Error

Unexpected error: server.error.

503 application/json Error

Temporarily unavailable, retry later: storage.unavailable.

API keys

Only the account owner, signed in with MailLogic (the Console or the webmail), can call this. API keys and OAuth application tokens are refused with 403 auth.sign_in_required, whatever scopes they hold. The Console uses these operations.

List API keys

GET /v1/api-keys listApiKeys

Account owner only

Only the account owner, signed in with MailLogic (the Console or the webmail), can call this. API keys and OAuth application tokens are refused with 403 auth.sign_in_required, whatever scopes they hold.

The customer's API keys that are not revoked, oldest first: for each, its scopes, its mailbox, when it expires and when it was last used. Secrets are never shown: a key is seen in full only in the response that creates it.

status is active or expired. last_used_at is kept to within 5 minutes. A customer holds at most 50 keys, so the list has no further pages. Requires a MailLogic sign-in (api-keys:read); an API key is refused with 403 auth.sign_in_required.

Responses

200 application/json ApiKeyList

Successful Response

401 application/json Error

No usable credential: auth.missing_token or auth.invalid_token.

403 application/json Error

The credential is not a MailLogic sign-in (auth.sign_in_required): API keys cannot manage API keys.

422 application/json Error

Invalid parameters: request.invalid.

429 application/json Error

auth.rate_limited: the customer made 120 API key requests (creating, listing and revoking keys) this minute. Retry after Retry-After seconds.

500 application/json Error

Unexpected error: server.error.

503 application/json Error

Temporarily unavailable, retry later: storage.unavailable.

Create an API key

POST /v1/api-keys createApiKey

Account owner only

Only the account owner, signed in with MailLogic (the Console or the webmail), can call this. API keys and OAuth application tokens are refused with 403 auth.sign_in_required, whatever scopes they hold.

Creates a Box API key for the customer and returns it, once.

Who. Only the customer's owner, signed in with its MailLogic account, manages API keys. A request made with an API key is refused with 403 auth.sign_in_required, whatever scopes the key holds: a key cannot create, list or revoke keys. The customer is the signed-in account's; no request names one.

Scopes. The key holds exactly the scopes in scopes: there are no default scopes. A key can be given domains:delete, domains:read, domains:write, drafts:read, drafts:write, events:read, junk:read, junk:release, junk:write, mailboxes:delete, mailboxes:read, mailboxes:write, messages:delete, messages:destroy, messages:read, messages:send, messages:write, usage:read, webhooks:read, webhooks:write. Each operation lists the scopes it needs as x-required-scopes. Any other value, api-keys:read and api-keys:write included, refuses the whole request with 422 api_keys.invalid_scope: no scope is dropped. A repeated scope counts once.

One mailbox. With mailbox_id, the key reaches only that mailbox, which must be one of the customer's (404 mailboxes.not_found otherwise): every other mailbox is reported to the key as not found. Such a key cannot hold the scopes that cover every mailbox, domains:delete, domains:read, domains:write, events:read, mailboxes:delete, mailboxes:write, usage:read, webhooks:read and webhooks:write (422 api_keys.incompatible_scope, which names them in params.scopes). Without mailbox_id, the key reaches every mailbox of the customer, including mailboxes created later.

Expiry. expires_at must be in the future and carry a time zone (422 api_keys.invalid_expiration). From then on, every request made with the key is refused with 401 auth.invalid_token, and it is listed as expired. Without expires_at, the key does not expire.

The key. secret is the key itself: send it as Authorization: Bearer <secret>, to this API or to Box's MCP server, where it is refused and allowed exactly as here. It is returned only in this response: store it now. Box cannot show it again. If it is lost, revoke the key and create another.

Limits. A customer can hold at most 50 keys. Revoked keys do not count; expired keys count until they are revoked (403 api_keys.limit_reached). Managing keys is also rate limited per customer (429 auth.rate_limited, with Retry-After): every creation request counts toward a limit of its own, and every request toward a limit that creating, listing and revoking share.

Requires a MailLogic sign-in (api-keys:write).

Request body application/json required

NewApiKey

FieldTypeDescription
expires_atstring (date-time) or null

When the key stops working: a future time, with a time zone. Omit it, or send null, for a key that does not expire.

default null

label requiredstring

A name for the key, so you can tell your keys apart: 1 to 100 characters on one line.

min length 1 · max length 100

mailbox_idstring or null

Restricts the key to this one mailbox of the customer (mbx_...). Omit it, or send null, for a key that reaches every mailbox.

max length 64 · default null

scopes requiredarray of string

What the key may do, chosen explicitly: at least one of domains:delete, domains:read, domains:write, drafts:read, drafts:write, events:read, junk:read, junk:release, junk:write, mailboxes:delete, mailboxes:read, mailboxes:write, messages:delete, messages:destroy, messages:read, messages:send, messages:write, usage:read, webhooks:read, webhooks:write.

min items 1 · max items 20

Responses

201 application/json CreatedApiKey

Successful Response

401 application/json Error

No usable credential: auth.missing_token or auth.invalid_token.

403 application/json Error

The credential is not a MailLogic sign-in (auth.sign_in_required): API keys cannot manage API keys, or the customer holds as many keys as it may (api_keys.limit_reached).

404 application/json Error

mailboxes.not_found: mailbox_id names no mailbox of the customer.

413 application/json Error

request.too_large: the request body is too large.

422 application/json Error

Invalid body (request.invalid), label (api_keys.invalid_label), scopes (api_keys.invalid_scope, api_keys.incompatible_scope) or expiry (api_keys.invalid_expiration).

429 application/json Error

auth.rate_limited: the customer made 120 API key requests (creating, listing and revoking keys) this minute, or 10 creation requests this hour (each creation request within the per-minute limit counts, invalid and refused ones included). Retry after Retry-After seconds.

500 application/json Error

Unexpected error: server.error.

503 application/json Error

Temporarily unavailable, retry later: storage.unavailable.

Revoke an API key

DELETE /v1/api-keys/{key_id} revokeApiKey

Account owner only

Only the account owner, signed in with MailLogic (the Console or the webmail), can call this. API keys and OAuth application tokens are refused with 403 auth.sign_in_required, whatever scopes they hold.

Revokes the key. Every request made with it from then on is refused with 401 auth.invalid_token, over this API and Box's MCP server alike. A request that was already under way when the key was revoked may still complete.

Revoking cannot be undone, and a revoked key is no longer listed. Another customer's key, a revoked key and a key that does not exist are all 404 api_keys.not_found. Requires a MailLogic sign-in (api-keys:write); an API key is refused with 403 auth.sign_in_required.

Parameters

NameInTypeDescription
key_id requiredpathstring

Responses

204

Successful Response

401 application/json Error

No usable credential: auth.missing_token or auth.invalid_token.

403 application/json Error

The credential is not a MailLogic sign-in (auth.sign_in_required): API keys cannot manage API keys.

404 application/json Error

api_keys.not_found: no such key, or it is already revoked.

422 application/json Error

Invalid parameters: request.invalid.

429 application/json Error

auth.rate_limited: the customer made 120 API key requests (creating, listing and revoking keys) this minute; the creation limit never applies to revoking. Retry after Retry-After seconds.

500 application/json Error

Unexpected error: server.error.

503 application/json Error

Temporarily unavailable, retry later: storage.unavailable.

Authorizations

Only the account owner, signed in with MailLogic (the Console or the webmail), can call this. API keys and OAuth application tokens are refused with 403 auth.sign_in_required, whatever scopes they hold. The Console uses these operations.

Get an MCP client's authorization request

GET /v1/authorization-requests/{request_id} getAuthorizationRequest

Account owner only

Only the account owner, signed in with MailLogic (the Console or the webmail), can call this. API keys and OAuth application tokens are refused with 403 auth.sign_in_required, whatever scopes they hold.

An MCP client asking for access to the customer's mailboxes, as the consent page shows it to the owner. The application sends the owner's browser to the consent page with the request's id; the page signs the owner in and calls this with it. The client, where the browser returns afterwards and the account are MailLogic's record of the request, not anything the browser says. The first call ties the request to the signed-in account; for any other account it is not found.

Usually the response is the request (AuthorizationRequest): show the client, what it may do (access) and the mailboxes, and send the owner's decision to decideAuthorizationRequest with consent_token. If the owner already allowed this client and has not revoked it, there is nothing to decide: the response is AuthorizationRedirect, and the browser goes back to the client with the access allowed before.

Requires a MailLogic sign-in (authorizations:write).

Parameters

NameInTypeDescription
request_id requiredpathstring

Responses

200 application/json AuthorizationRequest or AuthorizationRedirect

Successful Response

401 application/json Error

No usable credential: auth.missing_token or auth.invalid_token.

403 application/json Error

The credential is not a MailLogic sign-in (auth.sign_in_required): API keys and MCP clients cannot grant or manage authorizations.

404 application/json Error

authorization_requests.not_found: no such open request for this account (another account's, expired, or none).

409 application/json Error

authorization_requests.closed: the request was decided already. Start again from the application.

422 application/json Error

Invalid parameters: request.invalid.

429 application/json Error

auth.rate_limited: the customer made 120 authorization requests this minute. Retry after Retry-After seconds.

500 application/json Error

Unexpected error: server.error.

503 application/json Error

Temporarily unavailable, retry later: storage.unavailable.

Allow or deny an MCP client's authorization request

POST /v1/authorization-requests/{request_id} decideAuthorizationRequest

Account owner only

Only the account owner, signed in with MailLogic (the Console or the webmail), can call this. API keys and OAuth application tokens are refused with 403 auth.sign_in_required, whatever scopes they hold.

The owner's decision on an MCP client's request, sent by the consent page. The response says where to send the browser: back to the client, with its authorization, or with the refusal.

Allow. The client may then use Box's MCP server with an OAuth access token, with exactly scopes (all of the request's access if omitted) and, with mailbox_id, that one mailbox only (every other mailbox is reported to it as not found). Allowing a client the owner already allowed replaces the earlier authorization. The client never gets owner access: it cannot manage API keys or authorizations, and it cannot read the event feed or webhooks.

Deny. The client gets no access, and the request is closed.

Once. A request is decided once: a repeated or concurrent decision is 409 authorization_requests.closed, and never creates a second authorization. client_id must be the client the owner was shown, and consent_token must come from that same request. A browser may send the decision from the consent page only.

Limits. Allowing clients is rate limited per customer (429 auth.rate_limited, with Retry-After): every allow counts toward a limit of its own, and every request toward a limit that all authorization requests share. Denying never counts toward the first.

Requires a MailLogic sign-in (authorizations:write).

Parameters

NameInTypeDescription
request_id requiredpathstring

Request body application/json required

AuthorizationDecision

FieldTypeDescription
client_id requiredstring

The client.id the owner was shown. It must be the request's client.

min length 1 · max length 255

consent_token requiredstring

The request's consent_token.

min length 1 · max length 128

decision requiredstring: allow, deny

allow or deny the client.

mailbox_idstring or null

Restricts the client to this one mailbox of the customer. Omit it, or send null, to allow every mailbox. Not with an account-wide scope (customer_wide). Ignored when denying.

max length 64 · default null

scopesarray of string or null

What to allow, from the request's access: at least one of domains:delete, domains:read, domains:write, drafts:read, drafts:write, junk:read, junk:release, junk:write, mailboxes:delete, mailboxes:read, mailboxes:write, messages:delete, messages:destroy, messages:read, messages:send, messages:write, usage:read. Omit it to allow the mailbox capabilities, mailboxes:read, messages:read, messages:write (by_default): the account-wide ones (customer_wide), sending, deleting mail, Junk and drafts are allowed only when listed. Ignored when denying.

min items 1 · max items 17 · default null

Responses

200 application/json AuthorizationRedirect

Successful Response

401 application/json Error

No usable credential: auth.missing_token or auth.invalid_token.

403 application/json Error

The credential is not a MailLogic sign-in (auth.sign_in_required): API keys and MCP clients cannot grant or manage authorizations, the consent token is not this request's (authorization_requests.invalid_consent_token), or the decision was not sent from the consent page (authorization_requests.origin_refused).

404 application/json Error

authorization_requests.not_found: no such open request for this account (another account's, expired, or none). mailboxes.not_found: mailbox_id names no mailbox of the customer.

409 application/json Error

authorization_requests.closed: the request was decided already, or expired. authorization_requests.client_mismatch: client_id is not the request's client.

413 application/json Error

request.too_large: the request body is too large.

422 application/json Error

Invalid body (request.invalid) or scopes (authorization_requests.invalid_scope).

429 application/json Error

auth.rate_limited: the customer made 120 authorization requests this minute, or allowed 10 clients this hour. Retry after Retry-After seconds.

500 application/json Error

Unexpected error: server.error.

503 application/json Error

Temporarily unavailable, retry later: storage.unavailable.

List MCP client authorizations

GET /v1/authorizations listAuthorizations

Account owner only

Only the account owner, signed in with MailLogic (the Console or the webmail), can call this. API keys and OAuth application tokens are refused with 403 auth.sign_in_required, whatever scopes they hold.

The MCP clients the customer's owner allowed and has not revoked, oldest first: for each, the client, what it may do and its mailbox. A client acts on Box's MCP server with these and nothing more. last_used_at is kept to within 5 minutes. Requires a MailLogic sign-in (authorizations:read); an API key or an MCP client is refused with 403 auth.sign_in_required.

Responses

200 application/json AuthorizationList

Successful Response

401 application/json Error

No usable credential: auth.missing_token or auth.invalid_token.

403 application/json Error

The credential is not a MailLogic sign-in (auth.sign_in_required): API keys and MCP clients cannot grant or manage authorizations.

422 application/json Error

Invalid parameters: request.invalid.

429 application/json Error

auth.rate_limited: the customer made 120 authorization requests this minute. Retry after Retry-After seconds.

500 application/json Error

Unexpected error: server.error.

503 application/json Error

Temporarily unavailable, retry later: storage.unavailable.

Revoke an MCP client authorization

DELETE /v1/authorizations/{authorization_id} revokeAuthorization

Account owner only

Only the account owner, signed in with MailLogic (the Console or the webmail), can call this. API keys and OAuth application tokens are refused with 403 auth.sign_in_required, whatever scopes they hold.

Revokes an MCP client's authorization. Every request the client makes to Box's MCP server from then on is refused with 401 auth.invalid_token, even with an access token that has not expired. A request that was already under way may still complete. The client's sign-in with MailLogic is ended too when the owner revoking it is the one who allowed it; if the client asks again, the owner decides again.

Revoking cannot be undone, and a revoked authorization is no longer listed. Another customer's authorization, a revoked one and one that does not exist are all 404 authorizations.not_found. Requires a MailLogic sign-in (authorizations:write).

Parameters

NameInTypeDescription
authorization_id requiredpathstring

Responses

204

Successful Response

401 application/json Error

No usable credential: auth.missing_token or auth.invalid_token.

403 application/json Error

The credential is not a MailLogic sign-in (auth.sign_in_required): API keys and MCP clients cannot grant or manage authorizations.

404 application/json Error

authorizations.not_found: no such authorization, or it is already revoked.

422 application/json Error

Invalid parameters: request.invalid.

429 application/json Error

auth.rate_limited: the customer made 120 authorization requests this minute; the limit on allowing clients never applies to revoking. Retry after Retry-After seconds.

500 application/json Error

Unexpected error: server.error.

503 application/json Error

Temporarily unavailable, retry later: storage.unavailable.

Billing

Only the account owner, signed in with MailLogic (the Console or the webmail), can call this. API keys and OAuth application tokens are refused with 403 auth.sign_in_required, whatever scopes they hold. The Console uses these operations.

Get the account's payment state

GET /v1/billing getBilling

Account owner only

Only the account owner, signed in with MailLogic (the Console or the webmail), can call this. API keys and OAuth application tokens are refused with 403 auth.sign_in_required, whatever scopes they hold.

The account's plan and the state of its recurring payment.

  • billing_status is the payment's state as Stripe reports it: active or trialing (in good standing); past_due, unpaid, paused or canceled (not in good standing); null when the account has never paid through Stripe Checkout.
  • restricted: while the plan's payment is not in good standing, creating a mailbox, adding a domain and sending answer 402 billing.payment_required. The plan and everything in the account stay as they are: existing mail is kept, mail keeps arriving, and mailboxes, domains and mail can still be read and deleted. It clears as soon as Stripe reports the payment in good standing again.
  • cancel_at_period_end and current_period_end describe the current paid period; null and false when there is none.
  • cancellation_pending, downgrade_target, downgrade_state and effective_at describe the owner's cancellation (cancelBilling): scheduled until the paid period ends at effective_at (the plan is not renewed, and is the account's in full until then); completed once the account is on Free; ended_early when the payment ended before effective_at (it keeps its plan, held back as above, until then); cleanup_required when the period ended and the account did not fit Free (it keeps its plan and everything stored, held back as above, and its mailboxes, domains and stored mail are held to Free's limits, so mail is refused temporarily while it stores more than Free allows; it moves once it fits and the owner cancels again, or the owner pays again); approved when it is not confirmed with Stripe yet (cancel again). The account moves to Free only at or after effective_at, and nothing is ever deleted for it. A cancellation the owner took back (resumeBilling) shows as none: the plan renews again.

Requires the account owner's MailLogic sign-in (billing:read): API keys and OAuth clients cannot read payment.

Responses

200 application/json AccountBilling

Successful Response

401 application/json Error

No usable credential: auth.missing_token or auth.invalid_token.

403 application/json Error

The credential is not the account owner's MailLogic sign-in (auth.sign_in_required): API keys and OAuth clients cannot read or prepare payment.

422 application/json Error

Invalid parameters: request.invalid.

500 application/json Error

Unexpected error: server.error.

503 application/json Error

Temporarily unavailable, retry later: storage.unavailable.

Cancel the paid plan at the end of the paid period

POST /v1/billing/cancel cancelBilling

Account owner only

Only the account owner, signed in with MailLogic (the Console or the webmail), can call this. API keys and OAuth application tokens are refused with 403 auth.sign_in_required, whatever scopes they hold.

Cancels the paid plan at the end of the period already paid for, and moves the account to the Free plan then, once it fits Free.

The owner can cancel whatever the account uses: nothing is measured, and nothing the account holds refuses it. Once accepted (cancellation_scheduled), the plan is not renewed and nothing more is charged; the account keeps Plus in full (its mailboxes, domains, stored mail and monthly email allowance) until effective_at, the end of the paid period. Nothing is refunded, ended early or deleted.

At effective_at the account moves to Free if it fits it: at most 3 mailboxes, 3 domains and 3 GB of stored mail, measured then (the month's email count never matters). If it does not (getBilling: downgrade_state cleanup_required), nothing is deleted or forced: it keeps its plan and everything stored, held back (402 billing.payment_required for new mailboxes, domains and sending), and its mailboxes, domains and stored mail are held to Free's limits, so mail sent to it is refused temporarily (452 4.2.2, which the sender's server retries) while it stores more than Free allows. Mail can still be read, moved and deleted, and mailboxes and domains deleted. Remove what is over Free and cancel again: it moves to Free at once (downgraded); while it is still over, cancelling again answers 409 entitlements.downgrade_blocked with each resource over Free's limit, and by how much, in params.blockers. Or pay for Plus again (createBillingCheckout).

The account moves to Free only at or after effective_at. If the payment ends earlier (getBilling: downgrade_state ended_early), the account keeps its plan, held back (402 billing.payment_required), and cancelling answers awaiting_effective_time until then; cancel again at or after effective_at and it moves to Free at once (downgraded) if it fits.

Cancelling again is safe and answers the same. Until the paid period ends, the owner can take the cancellation back (resumeBilling).

Takes no parameters. Requires the account owner's MailLogic sign-in (billing:write): API keys, OAuth clients and agents cannot cancel.

Responses

200 application/json PlanCancellation

Successful Response

401 application/json Error

No usable credential: auth.missing_token or auth.invalid_token.

403 application/json Error

The credential is not the account owner's MailLogic sign-in (auth.sign_in_required): API keys, OAuth clients and agents cannot cancel the plan.

409 application/json Error

entitlements.downgrade_blocked: the paid period has ended and the account does not fit the Free plan yet; params.blockers names each resource over its limit (used, limit, excess). Nothing was changed or deleted. billing.not_cancelable: no paid plan to cancel, or its payment is not in good standing (params.status).

413 application/json Error

request.too_large: the request body is too large.

422 application/json Error

request.invalid: a body other than none or {}; this operation takes no parameters.

429 application/json Error

auth.rate_limited: the account's owner cancelled or resumed the plan too often. Retry after Retry-After seconds.

500 application/json Error

Unexpected error: server.error.

503 application/json Error

entitlements.usage_unavailable: the paid period has ended and the account's stored mail could not be measured now (params.reason, params.mailbox_count); nothing was changed. billing.unavailable: payment is not available, or the cancellation is accepted but not yet confirmed with Stripe: cancel again. storage.unavailable.

Prepare a Stripe Checkout page for the Plus plan

POST /v1/billing/checkout createBillingCheckout

Account owner only

Only the account owner, signed in with MailLogic (the Console or the webmail), can call this. API keys and OAuth application tokens are refused with 403 auth.sign_in_required, whatever scopes they hold.

Prepares a Stripe Checkout page on which a person can pay for the Plus plan, $15 a month, recurring. Answers its url: give it to the person who pays. They review and approve the recurring payment on Stripe's page; Box never sees card details and has no payment form of its own.

Preparing the page charges nothing, authorizes nothing and changes nothing: the account stays on its plan until Stripe confirms the payment, and then moves to Plus by itself (getBilling shows it: plan starter). Coming back from the page changes nothing either. A page that is not used expires at expires_at; preparing a new one makes any earlier one unusable, so only one can be paid.

Takes no parameters: the price and where Stripe sends the person back are Box's own. An account that already pays for its plan, or whose payment is under way, is refused (409 billing.already_paying); one whose earlier payment has ended can pay again.

Requires the account owner's MailLogic sign-in (billing:write): API keys and OAuth clients cannot prepare payment, and no agent can approve it.

Responses

201 application/json BillingCheckout

Successful Response

401 application/json Error

No usable credential: auth.missing_token or auth.invalid_token.

403 application/json Error

The credential is not the account owner's MailLogic sign-in (auth.sign_in_required): API keys and OAuth clients cannot read or prepare payment.

409 application/json Error

billing.already_paying: the account already pays for its plan, or a payment for it is under way (params.status); no page was prepared. billing.checkout_in_progress: a page prepared earlier was just paid; retry after Retry-After seconds.

413 application/json Error

request.too_large: the request body is too large.

422 application/json Error

request.invalid: a body other than none or {}; this operation takes no parameters.

429 application/json Error

auth.rate_limited: the account's owner prepared too many pages. Retry after Retry-After seconds.

500 application/json Error

Unexpected error: server.error.

503 application/json Error

billing.unavailable: no page could be prepared now (or payment is not available); nothing was changed. storage.unavailable.

Resume the paid plan's automatic renewal

POST /v1/billing/resume resumeBilling

Account owner only

Only the account owner, signed in with MailLogic (the Console or the webmail), can call this. API keys and OAuth application tokens are refused with 403 auth.sign_in_required, whatever scopes they hold.

Takes back the owner's cancellation of the paid plan while the period already paid for runs: the plan renews automatically at the end of the period again (renews_at), and its monthly payment is charged then, as before the cancellation. Nothing is charged now, and no new payment page is needed: the payment the person approved goes on. The account keeps Plus as it is; getBilling then shows no cancellation.

A plan that renews already answers renewing too: resuming again is safe.

It cannot be resumed once the paid period has ended, or within a minute of its end (409 billing.not_resumable, params.reason ended or period_ending): pay for Plus again once it has ended (createBillingCheckout). Nor without a plan paid for through Stripe Checkout, while its payment is not in good standing, or when its end was set some other way (not_renewable).

Takes no parameters. Requires the account owner's MailLogic sign-in (billing:write): API keys, OAuth clients and agents cannot resume it.

Responses

200 application/json PlanRenewal

Successful Response

401 application/json Error

No usable credential: auth.missing_token or auth.invalid_token.

403 application/json Error

The credential is not the account owner's MailLogic sign-in (auth.sign_in_required): API keys, OAuth clients and agents cannot resume the plan.

409 application/json Error

billing.not_resumable: the plan cannot be renewed again (params.reason): ended (its payment has ended: pay for Plus again), period_ending (its paid period ends within a minute), or not_renewable (no plan paid for through Stripe Checkout, its payment is not in good standing, or its end was set some other way); params.status is the payment's state. Nothing was changed.

413 application/json Error

request.too_large: the request body is too large.

422 application/json Error

request.invalid: a body other than none or {}; this operation takes no parameters.

429 application/json Error

auth.rate_limited: the account's owner cancelled or resumed the plan too often. Retry after Retry-After seconds.

500 application/json Error

Unexpected error: server.error.

503 application/json Error

billing.unavailable: payment is not available, or Stripe could not be asked or has not confirmed the renewal yet; nothing was changed. Retry after Retry-After seconds. storage.unavailable.

Schemas

Access

FieldTypeDescription
by_default requiredboolean

It is allowed when the owner allows the client without choosing (offered checked). Account-wide capabilities, sending, deleting and permanently deleting mail, Junk and drafts are not: they are allowed only when the owner chooses them.

customer_wide requiredboolean

It covers the whole account, not mailboxes: it cannot be allowed together with one mailbox, and it is allowed only when the owner chooses it.

description requiredstring

What it lets the client do.

scope requiredstring

The scope.

AccountBilling

FieldTypeDescription
billing_status requiredstring: incomplete, incomplete_expired, trialing, active, past_due, canceled, unpaid, paused or null

The state of the plan's recurring payment, as Stripe reports it: active or trialing (in good standing); past_due, unpaid, paused or canceled (new mailboxes, domains and outgoing mail are on hold while the plan is paid for this way: restricted). Null when the account has never paid through Stripe Checkout.

cancel_at_period_end requiredboolean

The recurring payment stops at the end of the current period.

cancellation_pending requiredboolean

The owner cancelled the plan and the account has not moved to downgrade_target yet. Until effective_at the account keeps its plan's limits in full. Once that has passed and it has not moved (cleanup_required, ended_early), new mailboxes, domains and stored mail are held to downgrade_target's limits, while the monthly email allowance stays the current plan's. Nothing is deleted.

current_period_end requiredstring (date-time) or null

When the current paid period ends; null when unknown.

downgrade_state requiredstring: approved, scheduled, ended_early, cleanup_required, completed or null

Where the owner's cancellation of the current paid period is: approved (accepted, not yet confirmed with Stripe: cancel again), scheduled (the plan ends at effective_at, and is not renewed; the account keeps it, in full, until then), ended_early (the payment ended before effective_at: the account keeps its plan, held back, until then; cancel again at or after effective_at), cleanup_required (the paid period ended and the account did not fit the target plan: it keeps its plan and everything stored, held back; remove what is over the target's limits, then cancel again, or pay for the plan again), completed (moved to the target plan). Null when there is none.

downgrade_target required"free" or null

The plan the owner's cancellation moves the account to; null when there is none.

effective_at requiredstring (date-time) or null

When the cancellation takes effect (the end of the paid period), or took effect (completed); null when there is none.

plan requiredstring

The account's plan: free or starter (the Plus plan).

restricted requiredboolean

New mailboxes, domains and outgoing mail are on hold until the payment is in good standing (billing.payment_required). Existing mail is kept, and mail keeps arriving.

stripe_backed requiredboolean

Whether the account's plan is paid for through Stripe.

AccountUsage

FieldTypeDescription
domains requiredResourceQuota

Domains in the account, whatever their status, each counting until it is deleted, against the plan's domain limit (entitlements.domain_limit).

emails requiredEmailQuota

Recipients sent to this month, against the plan's monthly email limit (entitlements.email_limit).

mailboxes requiredResourceQuota

Mailboxes, being created or active (suspended ones included), against the plan's mailbox limit (entitlements.mailbox_limit).

plan requiredstring

The account's plan: free (every account's until it moves) or starter (the Plus plan).

storage requiredStorageQuota

Stored mail, against the plan's storage limit (entitlements.storage_limit), as last measured.

ApiKey

FieldTypeDescription
created_at requiredstring (date-time)
expires_at requiredstring (date-time) or null

When the key stops working; null if it does not expire.

id requiredstring

The key's Box id. The key itself is boxk_, these same 24 characters after key_, and more, so you can tell which key a credential is.

label requiredstring

The name given to the key when it was created.

last_used_at requiredstring (date-time) or null

When the key was last used, to within 5 minutes; null if it has never been used.

mailbox_id requiredstring or null

The one mailbox the key is restricted to; null if it reaches every mailbox of the customer.

scopes requiredarray of string

What the key may do, in alphabetical order.

status requiredstring: active, expired

active: the key works. expired: its expires_at has passed, and every request made with it is refused. Revoke it to remove it from the list.

ApiKeyList

FieldTypeDescription
data requiredarray of ApiKey

The customer's API keys that are not revoked, oldest first. Their secrets are not shown.

Attachment

FieldTypeDescription
content_type requiredstring

The attachment's media type as the sender stated it, such as application/pdf. Untrusted.

filename requiredstring or null

The file name the sender gave, if any. Untrusted.

id requiredstring

The attachment's id (att_...): getAttachment downloads its content. It names this attachment of this message only, and stays the same on every read.

size requiredinteger

Size in bytes. For a text attachment in a character set other than UTF-8, the size of its text in UTF-8, which can differ from its download's length.

Authorization

FieldTypeDescription
client_id requiredstring

The MCP client's OAuth client id, as MailLogic registered it.

client_name requiredstring or null

The name the MCP client registered with, if any. It is chosen by the client: do not rely on it alone.

created_at requiredstring (date-time)

When the owner allowed it.

id requiredstring

The authorization's Box id.

last_used_at requiredstring (date-time) or null

When the client last used it, to within 5 minutes; null if it has not yet.

mailbox_id requiredstring or null

The one mailbox the client is restricted to; null if it reaches every mailbox of the customer.

scopes requiredarray of string

What the client may do, in alphabetical order.

AuthorizationDecision

FieldTypeDescription
client_id requiredstring

The client.id the owner was shown. It must be the request's client.

min length 1 · max length 255

consent_token requiredstring

The request's consent_token.

min length 1 · max length 128

decision requiredstring: allow, deny

allow or deny the client.

mailbox_idstring or null

Restricts the client to this one mailbox of the customer. Omit it, or send null, to allow every mailbox. Not with an account-wide scope (customer_wide). Ignored when denying.

max length 64 · default null

scopesarray of string or null

What to allow, from the request's access: at least one of domains:delete, domains:read, domains:write, drafts:read, drafts:write, junk:read, junk:release, junk:write, mailboxes:delete, mailboxes:read, mailboxes:write, messages:delete, messages:destroy, messages:read, messages:send, messages:write, usage:read. Omit it to allow the mailbox capabilities, mailboxes:read, messages:read, messages:write (by_default): the account-wide ones (customer_wide), sending, deleting mail, Junk and drafts are allowed only when listed. Ignored when denying.

min items 1 · max items 17 · default null

AuthorizationList

FieldTypeDescription
data requiredarray of Authorization

The customer's active authorizations, oldest first.

AuthorizationRedirect

FieldTypeDescription
redirect_to requiredstring

Where to send the browser now: back to the client.

AuthorizationRequest

FieldTypeDescription
access requiredarray of Access

What the client may be allowed to do. The owner may allow all of it or part of it.

client requiredRequestingClient

The MCP client asking for access.

consent_token requiredstring

Send it back with the decision.

email requiredstring or null

The MailLogic account the request is for.

id requiredstring

The authorization request's id, as the consent page received it.

mailboxes requiredarray of ConsentMailbox

The customer's mailboxes, one of which the client may be restricted to.

redirect_uri requiredstring

Where the browser returns to the client after the decision.

BillingCheckout

FieldTypeDescription
expires_at requiredstring (date-time)

When the page stops working. Prepare a new one after that.

url requiredstring

The Stripe Checkout page. Give it to the person who pays: they review and approve the $15/month recurring payment there. Opening it charges nothing; the plan becomes Plus only once Stripe confirms the payment.

ConsentMailbox

FieldTypeDescription
address requiredstring

Its email address.

id requiredstring

The mailbox's Box id.

CreatedApiKey

FieldTypeDescription
created_at requiredstring (date-time)
expires_at requiredstring (date-time) or null

When the key stops working; null if it does not expire.

id requiredstring

The key's Box id. The key itself is boxk_, these same 24 characters after key_, and more, so you can tell which key a credential is.

label requiredstring

The name given to the key when it was created.

last_used_at requiredstring (date-time) or null

When the key was last used, to within 5 minutes; null if it has never been used.

mailbox_id requiredstring or null

The one mailbox the key is restricted to; null if it reaches every mailbox of the customer.

scopes requiredarray of string

What the key may do, in alphabetical order.

secret requiredstring

The API key itself: send it as Authorization: Bearer <secret>. Returned only here, once: store it now. Box cannot show it again.

status requiredstring: active, expired

active: the key works. expired: its expires_at has passed, and every request made with it is refused. Revoke it to remove it from the list.

CreatedWebhook

FieldTypeDescription
created_at requiredstring (date-time)
event_cursor requiredstring

Pass as after to GET /v1/events to read the events this webhook has not delivered yet, in the order it delivers them. For a new webhook: the events after the ones on the feed when it was created.

failing_since requiredstring (date-time) or null

When attempts started failing, while they keep failing; null after a successful delivery or before any failed one.

id requiredstring

The webhook's Box id.

last_delivered_at requiredstring (date-time) or null

When an event was last delivered to it; null before the first delivery.

secret requiredstring

The webhook's signing secret. Returned only here, once: store it now. Box signs every delivery with it (see createWebhook).

state requiredstring: active, failed

active: Box delivers events to it, retrying failed attempts. failed: every attempt failed for 3 days, and Box no longer delivers to it. The events it did not deliver are on GET /v1/events after event_cursor. Delete it and create a new webhook to resume.

url requiredstring

Where Box delivers the events.

DiscardedDraft

FieldTypeDescription
id requiredstring
state required"discarded"
version requiredinteger

The version that was discarded.

DnsRecord

FieldTypeDescription
name requiredstring

The record's fully qualified name.

type requiredstring: TXT, MX, CNAME
value requiredstring

DnsRecordCheck

FieldTypeDescription
found requiredarray of string

What the DNS holds there (MX: the mail servers). DMARC, for a domain with no record of its own: the record of the domain whose policy it follows (RFC 9989: its organizational domain's).

record requiredDomainDnsRecord

The record checked, as in the domain's dns_records.

status requiredstring: ok, missing, mismatch, multiple, invalid

ok. missing: no such record. mismatch: it is there with another value (SPF: without the required term). multiple: more than one SPF or DMARC record (combine them). invalid: the DMARC record has no valid policy.

Domain

FieldTypeDescription
created_at requiredstring (date-time)
dns_checked_at requiredstring (date-time) or null

When Box last checked the domain's mail records.

dns_records requiredarray of DomainDnsRecord or null

Every record the domain's DNS holds for mail through Box: ownership, MX, SPF, two DKIM CNAMEs and DMARC. Null until the domain is set up for mail (pending_dns or active): an MX record pointing at Box before that would bounce the domain's mail.

id requiredstring

The domain's Box id.

mail_readiness requiredDomainMailReadiness

Whether the domain's mailboxes receive and send mail now, and what Box last saw in its DNS.

name requiredstring

The domain name, lower-cased.

ownership_checked_at requiredstring (date-time) or null

When Box last found out whether the verification record is in the domain's DNS.

ownership_status requiredstring: pending, verified, revoked

pending: ownership has not been proved yet. verified: the verification record was found. revoked: ownership was verified and no longer is; it can be verified again with the new verification record.

ownership_verified_at requiredstring (date-time) or null

When ownership was last verified.

status requiredstring: pending_ownership, provisioning, pending_dns, active, deleting

What is left to do. pending_ownership: ownership is not verified (pending or revoked): add verification_record to the domain's DNS and verify it. provisioning: ownership is verified and Box is setting the domain up for mail; verifying again or checking its DNS finishes it. pending_dns: set up; add dns_records to the domain's DNS and check them. active: a check found every record as required since ownership was last verified: mailboxes are created on it, and mail_readiness says whether they receive and send now (a later check that finds a record wrong does not undo active). deleting: its deletion has begun and has not finished; deleting it again finishes it.

verification_record requiredDnsRecord

The TXT record that proves ownership of this domain for this account. Keep it in the domain's DNS. It changes when ownership is revoked: the earlier record no longer proves ownership.

DomainConnect

FieldTypeDescription
available requiredboolean

Whether the domain's DNS provider can set the records of stage up now, once the person approves them there. False: reason says why, and the records are added by hand as verification_record and dns_records show them.

blockers requiredarray of DomainConnectBlocker

What prevents the changes: the records are then added by hand.

changes requiredarray of DomainConnectChange

Every record of the stage and what it would do.

dns_zone requiredstring or null

The DNS zone the domain is in.

domain_id requiredstring

The domain's Box id.

host requiredstring or null

The domain's name below dns_zone ("" for the zone itself).

provider requiredDomainConnectProvider or null

The DNS provider that would make the changes.

reason requiredstring: not_configured, provider_unknown, provider_unsupported, not_authoritative, template_unavailable, provisioning, already_active, deleting, blocked, already_configured or null

Why it is not available (null when it is): not_configured (not offered by this Box environment), provider_unknown (the domain's DNS provider could not be found), provider_unsupported (its DNS provider does not support this), not_authoritative (the domain's DNS is shared between providers), template_unavailable (its DNS provider does not offer Box's records), provisioning (the domain is being set up for mail: verify or check it first), already_active, deleting, blocked (see blockers), already_configured (every record is already as needed: check the domain).

requires_mx_acknowledgment requiredboolean

The changes replace the domain's MX records (mx_replaces_existing): preparing them needs accept_mx_change, once the person has agreed that the domain's incoming mail moves to Box.

stage requiredstring: ownership, mail or null

What can be set up now: ownership (the verification record, while ownership is not verified) or mail (the mail records, once the domain is pending_dns). Null when nothing can be.

warnings requiredarray of DomainConnectWarning

What the person should know before approving.

DomainConnectAuthorization

FieldTypeDescription
authorization_url requiredstring

The DNS provider's page where the person signs in and approves the changes. Open it in the person's own browser; nothing changes until they approve there. It does not expire and can be opened again (the same changes again add nothing), and revoking the credential that prepared it does not withdraw it: prepare a new one after the domain changes. It is signed, and the DNS provider refuses it if any character changes: an app that rewrites the links it shows (a chat app, for example) breaks it. Such an app gives the person console_url instead.

changes requiredarray of DomainConnectChange

Every record of the stage and what it would do, as the domain's availability reports them.

console_url requiredstring

MailLogic Console's page for this domain's automatic setup, on Box's own site: the address to give the person when the link reaches them through an app that may rewrite it, such as an AI assistant's chat. Signed in with the account, the person sees the changes there and goes on to the DNS provider from it. It names only the domain and the stage, and works with parameters an app adds to it.

dns_zone requiredstring
groups requiredarray of string: verification, mx, dkim, spf, dmarc

The record groups the page applies.

host requiredstring
next_check requiredstring: ownership, dns

What establishes the result once the person has approved: ownership (verify the domain's ownership) or dns (check its DNS). Coming back from the DNS provider proves nothing.

provider requiredDomainConnectProvider
requires_user_approval requiredtrue

Always true: only the person, signed in at the DNS provider, can approve the changes.

stage requiredstring: ownership, mail
warnings requiredarray of DomainConnectWarning

As the domain's availability reports them.

DomainConnectBlocker

FieldTypeDescription
code requiredstring: cname_at_name, cname_at_record, spf_multiple, spf_redirect, spf_invalid, spf_too_many_lookups, dkim_selector_in_use

cname_at_name: The domain's name has a CNAME record (a website, for example), which the MX record would delete. cname_at_record: A CNAME record is at the name of a record that would be added (params.name); it would be deleted. spf_multiple: The domain has more than one SPF record (params.count): combine them first. spf_redirect: The domain's SPF record uses redirect=: add Box's term yourself. spf_invalid: The domain's SPF record could not be read safely (an unknown term, or terms after all). spf_too_many_lookups: With Box's term, the domain's SPF record would need more than 10 DNS lookups. dkim_selector_in_use: A record Box needs for DKIM (params.name) already holds another service's record (params.current), which would be replaced.

params requiredobject

Its details: the record's name, what the DNS holds now (current), and the like.

DomainConnectChange

FieldTypeDescription
action requiredstring: add, replace, merge, keep, unchanged

What it does to the domain's DNS as it is now: add (nothing there), replace (current is removed), merge (Box's term is added to the SPF record in current, which then ends ~all), keep (the domain's own record stays and nothing is written: DMARC), unchanged (it is already so). Only add, replace and merge change anything.

current requiredarray of string

What the domain's DNS holds there now (MX: preference and host).

group requiredstring: verification, mx, dkim, spf, dmarc

The record's group: verification (ownership), mx, dkim, spf or dmarc.

name requiredstring

The record's fully qualified name.

priority requiredinteger or null

The MX preference; null for other records.

type requiredstring: TXT, MX, CNAME
value requiredstring

The record the DNS provider writes; for merge (SPF), the term it adds to the domain's SPF record.

DomainConnectProvider

FieldTypeDescription
id requiredstring

The DNS provider's Domain Connect identifier (its own domain).

name requiredstring

The DNS provider's name, to show the person.

DomainConnectWarning

FieldTypeDescription
code requiredstring: mx_replaces_existing, mx_starts_receiving, spf_all_softened, spf_all_changed, previous_ownership_records, dkim_previous_records, dmarc_preserved, dmarc_invalid, dmarc_multiple, dmarc_inherited_strict, mail_hosts_differ

mx_replaces_existing: The domain's MX records (params.current) are replaced: all its incoming mail moves to Box when the changes are approved, and stops arriving where it arrives now. Addresses with no mailbox in Box yet are refused until their mailboxes are created, which can only be done once the domain is active. Needs accept_mx_change. mx_starts_receiving: The domain has no MX record: it starts receiving mail at Box. Addresses with no mailbox in Box are refused. spf_all_softened: The domain's SPF record ends -all; with Box's term added it ends ~all. spf_all_changed: The domain's SPF record's all term (params.from, null for none) becomes ~all. previous_ownership_records: The domain has earlier verification records (params.count); the new one is added beside them. They no longer prove ownership and can be removed. dkim_previous_records: A DKIM record (params.name) points at an earlier domain's records in Box (params.current); it is replaced. dmarc_preserved: The domain's own valid DMARC record is kept (params.policy); no DMARC record is added. dmarc_invalid: The domain's DMARC record has no valid policy. It is left as it is: fix it, or the domain will not become ready. dmarc_multiple: The domain has more than one DMARC record (params.count). They are left as they are: combine them, or the domain will not become ready. dmarc_inherited_strict: The domain has no DMARC record of its own and follows a strict policy (params.policy) from params.from. It is kept: no p=none record is added, so that policy is not weakened, and the domain is ready with it. mail_hosts_differ: This Box environment checks for other mail hosts than the ones the DNS provider adds: with these records the domain does not become ready here.

params requiredobject

Its details: the record's name, what the DNS holds now (current), and the like.

DomainDnsCheck

FieldTypeDescription
domain requiredDomain

The domain as it is after the check.

records requiredarray of DnsRecordCheck

One per mail record: MX, SPF, both DKIM records and DMARC.

result requiredstring: ready, not_ready

ready: every mail record is as required; with ownership verified, the domain is active. not_ready: see records. A domain that is not active yet becomes active once a check finds it ready. On an active domain it changes what domain.mail_readiness reports, never active.

DomainDnsRecord

FieldTypeDescription
name requiredstring

The record's fully qualified name.

priority requiredinteger or null

The MX preference; null for other records.

purpose requiredstring: ownership, mx, spf, dkim, dmarc

What the record is for: ownership (the verification record), mx (mail to the domain comes to Box), spf (Box may send for the domain), dkim (Box signs the domain's mail; two records), dmarc (the domain's DMARC policy).

required_mechanism requiredstring or null

SPF only: the term Box needs in the domain's SPF record. Null for other records.

requirement requiredstring: exact, require_mechanism, preserve_or_create

exact: the domain's DNS holds this record. require_mechanism: the domain's one SPF record contains required_mechanism before any all term; the rest of the SPF record is yours. preserve_or_create: keep the domain's own valid DMARC record, or add this one. A domain with none of its own follows the DMARC record RFC 9989 finds for it: its organizational domain's (the highest parent domain with one, unless a psd tag says otherwise). That record with a valid policy is enough.

type requiredstring: TXT, MX, CNAME
value requiredstring

exact: the record's value. require_mechanism (SPF): the complete record to add if the domain has no SPF record. preserve_or_create (DMARC): the record to add if the domain has no DMARC record and follows no other domain's.

DomainList

FieldTypeDescription
data requiredarray of Domain

The account's domains, oldest first.

DomainMailReadiness

(DNS decoupling) Whether the domain's mailboxes receive and send now, and what Box last saw in its DNS.

FieldTypeDescription
inbound requiredstring: ready, not_ready

ready: the domain's mailboxes accept incoming mail: ownership is verified and Box has set the domain up for mail. Its DNS records never change this: a check that finds them wrong does not stop incoming mail. not_ready: incoming mail for its mailboxes is deferred.

last_attempt requiredstring: answered, unavailable or null

Whether the DNS answered the last check (unavailable: it did not, and nothing was concluded from it). Null before any check.

last_attempt_at requiredstring (date-time) or null

When the last check began, answered or not.

outbound requiredstring: unverified, valid, degraded, invalid

Whether the domain's sending records (SPF, and the DKIM record Box signs with) are as required. unverified: the domain is not active, or no check has found them as required since its ownership was last verified: its mailboxes send nothing yet. valid: found as required. degraded: a later check found them wrong; its mailboxes still send. invalid: repeated checks over at least 30 minutes confirmed it, and its mailboxes' new sends are refused (sending). Fix them and check the DNS again: a check that finds them as required, confirmed by a second, independent DNS lookup, makes the domain valid again. Box checks active domains every 15 minutes, and every 5 while they are degraded or invalid. A check that finds them as required but that the second lookup does not confirm yet (DNS changes take a while to be seen everywhere) changes nothing: check again later. A DNS that does not answer never changes it either way.

outbound_verified_at requiredstring (date-time) or null

When a check last found the sending records as required since ownership was last verified.

records requiredobject of string: ok, missing, mismatch, multiple, invalid or null

What the last answered check found, by record: mx, spf, dkim_box1, dkim_box2 and dmarc, each ok, missing, mismatch, multiple or invalid. Null before any.

sending requiredstring: allowed, not_ready, suspended, unconfirmed

Whether the domain's DNS lets its mailboxes send new mail now. allowed. not_ready: the domain is not active, or outbound is unverified (sends are refused with 403 mailboxes.domain_inactive). suspended: outbound is invalid (sends are refused with 403 domains.outbound_dns_invalid until the records are fixed and checked). unconfirmed: Box has not been able to confirm the sending records for 7 days, because the DNS did not answer (sends are refused for now with 503 domains.outbound_dns_unconfirmed; the next check that finds them as required lifts it). Incoming mail, reading and mail already accepted for delivery are never affected by it. Other reasons a send can be refused (the account's plan, its payment, sending limits, a suspension by the mail service) are not reported here.

warnings requiredarray of string

On an active domain, the records of records that are not ok. MX, DMARC and dkim_box2 are reported only: they never stop mail.

DomainOwnershipCheck

FieldTypeDescription
domain requiredDomain

The domain as it is after the check: status says whether it is set up for mail yet.

result requiredstring: verified, already_verified, record_missing, value_mismatch

verified: this check verified ownership. already_verified: ownership was already verified. record_missing: there is no TXT record at the verification record's name. value_mismatch: there are TXT records at that name, but none has the expected value.

Draft

FieldTypeDescription
attachments requiredarray of DraftAttachment
author requiredDraftActor

Who created the draft.

bcc requiredarray of string
cc requiredarray of string or null

Null: a reply's Cc is left to Box.

created_at requiredstring (date-time)
id requiredstring

The draft's id (drf_...), the same for its whole life, whoever saves it.

include_original_attachments requiredboolean
kind requiredstring: new, reply, reply_all, forward
mailbox_id requiredstring
message_id requiredstring or null

A reply's or forward's message (msg_...); null for a new message.

original_attachment_ids requiredarray of string or null
state requiredstring: open, submitting

open: it can be edited. submitting: it is being sent, and cannot be changed now.

subject requiredstring or null

Null: a reply's or forward's subject is left to Box.

text requiredstring
thread_id requiredstring or null

A reply's conversation (thr_...), its message's; null otherwise.

to requiredarray of string or null

Null: a reply's recipients are left to Box.

updated_at requiredstring (date-time)

When its current version was saved.

updated_by requiredDraftActor

Who saved its current version. Review a draft another client saved before sending it.

version requiredinteger

Increases by one with every save. Saving and discarding name it.

DraftActor

FieldTypeDescription
name requiredstring or null

The API key's label, or the application's name as it registered it (untrusted text); null for the owner, or once the key or authorization is gone.

type requiredstring: user, api_key, oauth_client

Who: the account's owner signed in (user), an API key, or an application the owner authorized (oauth_client).

you requiredboolean

Whether that was the credential making this request.

DraftAttachment

FieldTypeDescription
charset requiredstring or null

The character set of a text file, if it states one.

content_type requiredstring
filename requiredstring or null
id requiredstring

The attachment's id in this draft (att_...): the same on every version of the draft. Keep it with {"id": ...} when saving; download it with getDraftAttachment.

size requiredinteger

Its size in bytes, decoded.

DraftPage

FieldTypeDescription
data requiredarray of DraftSummary
next_cursor requiredstring or null

DraftSend

FieldTypeDescription
version requiredinteger

The version to send: the version of the draft as you (or the person you act for) last read and reviewed it. If it was saved since, it is 409 drafts.version_conflict and nothing is sent.

min 1

DraftSummary

FieldTypeDescription
attachment_count requiredinteger
author requiredDraftActor

Who created the draft.

bcc requiredarray of string
cc requiredarray of string or null

Null: a reply's Cc is left to Box.

created_at requiredstring (date-time)
id requiredstring

The draft's id (drf_...), the same for its whole life, whoever saves it.

kind requiredstring: new, reply, reply_all, forward
mailbox_id requiredstring
message_id requiredstring or null

A reply's or forward's message (msg_...); null for a new message.

preview requiredstring

The start of its text.

state requiredstring: open, submitting

open: it can be edited. submitting: it is being sent, and cannot be changed now.

subject requiredstring or null

Null: a reply's or forward's subject is left to Box.

thread_id requiredstring or null

A reply's conversation (thr_...), its message's; null otherwise.

to requiredarray of string or null

Null: a reply's recipients are left to Box.

updated_at requiredstring (date-time)

When its current version was saved.

updated_by requiredDraftActor

Who saved its current version. Review a draft another client saved before sending it.

version requiredinteger

Increases by one with every save. Saving and discarding name it.

DraftUpdate

FieldTypeDescription
attachmentsarray of KeptDraftAttachment or OutgoingAttachment

The draft's own files, in order, at most 10 and 10 MiB in all: each either a new file (filename, content_type, content_base64, optional charset) or {"id": "att_..."}, one the draft already has, kept as it is without sending its content again. A file of the draft left out is removed. A forward's message's own attachments are not here (original_attachment_ids).

max items 10

bccarray of string (email)

Blind copies: addresses the message will be sent to without any recipient seeing them. At most 50 in each field and 100 in all.

max items 50

ccarray of string (email)

The draft's Cc; as to (for a reply, [] is no Cc, leaving it out keeps Box's).

max items 50

include_original_attachmentsboolean

Only for a forward: whether the forwarded message's own attachments go with it when it is sent. false forwards its text only.

default true

original_attachment_idsarray of string

Only for a forward: which of the forwarded message's own attachments go, by the id its read lists each with (att_...), each once. [] sends none. Leave it out to send them all. Not with include_original_attachments: false.

max items 10

subjectstring

The subject. For a reply or a forward, leave it out to keep Box's (Re: or Fwd: and the message's); for a new message, leaving it out is "".

max length 998

textstring

The plain-text body, as it will be sent. For a forward, the note above the forwarded message, which Box adds when it is sent.

max length 1000000 · default ""

toarray of string (email)

The draft's To. A draft may be incomplete: no recipient yet is fine, and duplicates are checked when it is sent. For a reply, leave it out to keep Box's (who the reply would go to, worked out from the message when it is sent); for any other draft, leaving it out is [].

max items 50

version requiredinteger

The version this save edits: the version of the draft as you last read it. Another save since is 409 drafts.version_conflict, and nothing changes.

min 1

EmailQuota

FieldTypeDescription
limit requiredinteger

The plan's monthly email limit, in recipients.

period requiredstring

The month counted: YYYY-MM, a calendar month in UTC.

remaining requiredinteger

How many more the plan allows: limit minus used, and 0 (never negative) when used is at or over it.

reset_at requiredstring (date-time)

When the count starts again from 0: the first instant of the next calendar month, UTC.

used requiredinteger

Recipients of the mail the account sent this month: each address in to and cc of a message counts once. Can be more than limit (after a move to a smaller plan, for example); it is never cut to the limit.

EmptiedTrash

FieldTypeDescription
destroyed requiredinteger

How many messages were permanently deleted from the trash now. They cannot be recovered, and the storage they used is free.

mailbox_id requiredstring

The mailbox whose trash was emptied.

skipped requiredinteger

How many messages in the trash were left as they are because they are in another folder too. Deleting such a message (deleteMessage) leaves it in the trash only, and emptying the trash then deletes it.

Error

FieldTypeDescription
detail requiredErrorDetail

ErrorDetail

FieldTypeDescription
code requiredstring

Stable, machine-readable error code, such as mailboxes.not_found. Branch on this, not on the message.

message requiredstring

Human-readable explanation. May change.

params requiredobject

Details for some codes, for example the required_scope of auth.insufficient_scope.

request_id requiredstring

Identifies this request in support conversations.

Event

FieldTypeDescription
created_at requiredstring (date-time)

When Box recorded the event.

data requiredobject

Details for the event type. message.received: folder, from, subject, received_at and size (bytes) of the message, and released_from_junk: true for a message that arrived in Junk and was marked as not spam (mail in Junk is not announced until then). message.delivered and message.delivery_canceled: recipient. message.deferred and message.delivery_failed: recipient, status (the mail status code, such as 5.1.1, or null) and reason (recipient_address, recipient_mailbox, mail_system, network, protocol, content, policy, refused_temporarily or other; see sendMessage).

id requiredstring

The event's Box id. Use it to discard an event you have already handled.

mailbox_id requiredstring

The mailbox the event concerns.

message_id requiredstring or null

The message the event concerns, if any. Read it with GET /v1/mailboxes/{mailbox_id}/messages/{message_id}; a message permanently deleted since answers 404 messages.not_found.

type requiredstring

The kind of event: message.received (a message arrived in a mailbox), or, for a message the mailbox sent, message.deferred, message.delivered, message.delivery_failed or message.delivery_canceled (see sendMessage). More types will be added; skip types you do not handle.

EventPage

FieldTypeDescription
data requiredarray of Event

Events after after, oldest first; at most limit.

has_more requiredboolean

More events are available now; request again right away.

next_cursor requiredstring

Pass as after to continue after the last event in data. Unchanged from after when data is empty.

ForwardMessage

FieldTypeDescription
attachmentsarray of OutgoingAttachment

New files to attach, in addition to the forwarded message's own attachments. With those, at most 10 files and 10 MiB in total.

max items 10

bccarray of string (email)

Addresses to send a blind copy to: they get the message, and no recipient sees them (only the mailbox's own copy shows them). Each recipient may appear only once across to, cc and bcc, at most 100 in all.

max items 50

ccarray of string (email)

Addresses to copy. Each recipient may appear only once across to, cc and bcc.

max items 50

include_original_attachmentsboolean

Whether the forwarded message's own attachments are attached too (after attachments). false forwards its text only.

default true

original_attachment_idsarray of string

Which of the forwarded message's own attachments go, by the id its read lists each with (att_...), each once: they are attached in the message's order. [] sends none of them. Leave it out to send them all. Not with include_original_attachments: false. An id the message does not list is 422 messages.original_attachment_unknown.

max items 10

subjectstring

The forward's subject, instead of Fwd: and the message's. Leave it out to keep Box's.

max length 998

textstring

A note, sent above the forwarded message. May be empty.

max length 1000000 · default ""

to requiredarray of string (email)

Recipient addresses.

min items 1 · max items 50

JunkDetails

FieldTypeDescription
expires_at requiredstring (date-time) or null

With retention expires: from when it may be deleted automatically, about 30 days after it went into Junk or the trash (moving it between the two does not start the 30 days again). Once deleted, it cannot be recovered. Null otherwise.

reasons requiredarray of string: spam_score, dmarc_fail, unverified, user, unknown

Why it was put in Junk: spam_score (it scored as spam), dmarc_fail (its sender failed DMARC, whatever its score), unverified (there is no evidence the mail service's inbound checks ran for it), user (it was marked as spam, markSpam), or unknown.

retention requiredstring: expires, kept, unknown

How long it is kept. expires: it is in Junk or the trash, and may be deleted automatically from expires_at; mark it as not spam (markNotSpam; from the trash, restore it first) to keep it. kept: it is stored elsewhere (held as unverified, in the Inbox, say) and is not deleted automatically. unknown: it is in Junk or the trash, but when its 30 days began is not known (it was moved there outside Box): it may be deleted automatically at any time.

since requiredstring (date-time)

When it was put in Junk: when it arrived, for mail held as it arrived; when it was marked as spam.

JunkMessage

FieldTypeDescription
folder requiredstring: inbox, sent, drafts, trash, junk, archive, other

Where the message is in the mailbox. other is any folder not listed.

from requiredstring or null

The sender's address, if the message names one.

from_name requiredstring or null

(display names) The name the message's From field gives with from, as the message says it: for mail the mailbox sent, the name it was sent with; for any other mail, what its sender wrote, which nothing verifies (anyone can write any name). Control and bidirectional formatting characters are shown as spaces, white space as one space, at most 256 characters. Null if it gives none.

has_attachments requiredboolean
id requiredstring

The message's Box id: the message_id of its events.

is_read requiredboolean

Whether the message is marked as read. New mail arrives unread; a message the mailbox sent is read. Change it with updateMessage.

junk requiredJunkDetails

Why the message is in Junk, and how long it is kept there.

mailbox_id requiredstring

The mailbox the message is in.

preview requiredstring

The start of the message's text, for display in a list.

received_at requiredstring (date-time)

When the message arrived in the mailbox (for mail the mailbox sent: when it was sent).

size requiredinteger

Size of the whole message in bytes.

subject requiredstring or null
thread_id requiredstring or null

The thread (thr_...) the message is in: the conversation of the messages it answers and that answer it, as getThread reads it, the mail the mailbox sent included. Null for a message not placed in one yet.

to requiredarray of string

The recipient addresses the message names in To.

JunkMessagePage

FieldTypeDescription
data requiredarray of JunkMessage

Messages in Junk, newest first; at most limit.

next_cursor requiredstring or null

Pass as cursor for the next page; null on the last page.

KeptDraftAttachment

FieldTypeDescription
id requiredstring

One of the draft's own attachments, by the id the draft lists it with.

pattern ^att_[0-9a-f]{32}$

Mailbox

FieldTypeDescription
address requiredstring

The mailbox's email address.

created_at requiredstring (date-time)
display_name requiredstring or null

(display names) The name the mailbox's mail is sent with, as display_name <address> in From: on every message, reply, forward and draft it sends, whoever sends it. Only the account owner sets it (updateMailbox); it never changes the address or what may send from it. Null: mail is sent from the bare address.

domain_id requiredstring

The id (dom_...) of the account's domain the address is on.

id requiredstring

The mailbox's Box id.

status requiredstring: active, suspended, deleting

suspended: the mailbox is not accepting incoming mail and cannot send (403 mailboxes.suspended). It stays readable. deleting: its deletion has begun and has not finished; it receives, sends and shows nothing any more, and deleting it again finishes it.

MailboxDetail

FieldTypeDescription
address requiredstring

The mailbox's email address.

created_at requiredstring (date-time)
display_name requiredstring or null

(display names) The name the mailbox's mail is sent with, as display_name <address> in From: on every message, reply, forward and draft it sends, whoever sends it. Only the account owner sets it (updateMailbox); it never changes the address or what may send from it. Null: mail is sent from the bare address.

domain_id requiredstring

The id (dom_...) of the account's domain the address is on.

id requiredstring

The mailbox's Box id.

status requiredstring: active, suspended, deleting

suspended: the mailbox is not accepting incoming mail and cannot send (403 mailboxes.suspended). It stays readable. deleting: its deletion has begun and has not finished; it receives, sends and shows nothing any more, and deleting it again finishes it.

usage requiredMailboxUsage

MailboxList

FieldTypeDescription
data requiredarray of Mailbox

The mailboxes the credential can access, oldest first.

MailboxUpdate

FieldTypeDescription
display_name requiredstring or null

The name the mailbox's mail is sent with, or null (or only white space) for none. Stored normalized (Unicode NFC, trimmed, runs of spaces made one). At most 64 characters and 200 bytes in UTF-8; any script, accents, emoji, quotes and punctuation are fine. Refused: control characters and line breaks, bidirectional overrides and isolates, anything that reads as an email address (@, <, >) and the encoded-word marker =?, which mail programs would show as something else.

MailboxUsage

FieldTypeDescription
measured_at requiredstring (date-time) or null

When Box last measured the mailbox; null before the first measurement. The figures can trail the most recent mail by a short time.

message_count requiredinteger

Number of messages in the mailbox.

storage_bytes requiredinteger

Storage the mailbox uses, in bytes.

Message

FieldTypeDescription
folder requiredstring: inbox, sent, drafts, trash, junk, archive, other

Where the message is in the mailbox. other is any folder not listed.

from requiredstring or null

The sender's address, if the message names one.

from_name requiredstring or null

(display names) The name the message's From field gives with from, as the message says it: for mail the mailbox sent, the name it was sent with; for any other mail, what its sender wrote, which nothing verifies (anyone can write any name). Control and bidirectional formatting characters are shown as spaces, white space as one space, at most 256 characters. Null if it gives none.

has_attachments requiredboolean
id requiredstring

The message's Box id: the message_id of its events.

is_read requiredboolean

Whether the message is marked as read. New mail arrives unread; a message the mailbox sent is read. Change it with updateMessage.

mailbox_id requiredstring

The mailbox the message is in.

preview requiredstring

The start of the message's text, for display in a list.

received_at requiredstring (date-time)

When the message arrived in the mailbox (for mail the mailbox sent: when it was sent).

size requiredinteger

Size of the whole message in bytes.

subject requiredstring or null
thread_id requiredstring or null

The thread (thr_...) the message is in: the conversation of the messages it answers and that answer it, as getThread reads it, the mail the mailbox sent included. Null for a message not placed in one yet.

to requiredarray of string

The recipient addresses the message names in To.

MessageDetail

FieldTypeDescription
attachments requiredarray of Attachment

The message's attachments, each with the id getAttachment downloads its content by.

bcc requiredarray of string

For a message the mailbox sent through Box: the addresses it was sent to as Bcc (blind copies), which no recipient sees. Empty for every other message, whatever it says.

cc requiredarray of string

The recipient addresses the message names in Cc.

folder requiredstring: inbox, sent, drafts, trash, junk, archive, other

Where the message is in the mailbox. other is any folder not listed.

from requiredstring or null

The sender's address, if the message names one.

from_name requiredstring or null

(display names) The name the message's From field gives with from, as the message says it: for mail the mailbox sent, the name it was sent with; for any other mail, what its sender wrote, which nothing verifies (anyone can write any name). Control and bidirectional formatting characters are shown as spaces, white space as one space, at most 256 characters. Null if it gives none.

has_attachments requiredboolean
html requiredstring or null

The HTML body; null if the message has none. Treat it as untrusted content.

id requiredstring

The message's Box id: the message_id of its events.

internet_message_id requiredstring or null

The message's Message-ID header, without the angle brackets; null if it has none.

is_read requiredboolean

Whether the message is marked as read. New mail arrives unread; a message the mailbox sent is read. Change it with updateMessage.

junkJunkDetails or null

For a message in Junk (readable only with junk:read): why it is there and how long it is kept. Null otherwise.

mailbox_id requiredstring

The mailbox the message is in.

preview requiredstring

The start of the message's text, for display in a list.

received_at requiredstring (date-time)

When the message arrived in the mailbox (for mail the mailbox sent: when it was sent).

recipients requiredarray of RecipientDelivery or null

For a message the mailbox sent: each recipient's delivery state now, in address order. Null when Box has no delivery state for the message: mail the mailbox received, and mail it sent before delivery was tracked or whose sending is not yet confirmed.

reply_to requiredarray of string

The addresses the message names in Reply-To: where a reply to it goes (replyToMessage). Empty if it names none; a reply then goes to from.

size requiredinteger

Size of the whole message in bytes.

subject requiredstring or null
text requiredstring or null

The plain-text body; null if the message has none.

thread_id requiredstring or null

The thread (thr_...) the message is in: the conversation of the messages it answers and that answer it, as getThread reads it, the mail the mailbox sent included. Null for a message not placed in one yet.

to requiredarray of string

The recipient addresses the message names in To.

MessagePage

FieldTypeDescription
data requiredarray of Message

Messages, newest first; at most limit.

next_cursor requiredstring or null

Pass as cursor for the next page; null on the last page.

MessageSearchResults

FieldTypeDescription
data requiredarray of Message

The matching messages, newest first; at most limit.

MessageUpdate

FieldTypeDescription
is_read requiredboolean

true marks the message as read, false as unread.

NewApiKey

FieldTypeDescription
expires_atstring (date-time) or null

When the key stops working: a future time, with a time zone. Omit it, or send null, for a key that does not expire.

default null

label requiredstring

A name for the key, so you can tell your keys apart: 1 to 100 characters on one line.

min length 1 · max length 100

mailbox_idstring or null

Restricts the key to this one mailbox of the customer (mbx_...). Omit it, or send null, for a key that reaches every mailbox.

max length 64 · default null

scopes requiredarray of string

What the key may do, chosen explicitly: at least one of domains:delete, domains:read, domains:write, drafts:read, drafts:write, events:read, junk:read, junk:release, junk:write, mailboxes:delete, mailboxes:read, mailboxes:write, messages:delete, messages:destroy, messages:read, messages:send, messages:write, usage:read, webhooks:read, webhooks:write.

min items 1 · max items 20

NewDomain

FieldTypeDescription
name requiredstring

The domain to add, such as example.com or mail.example.com: a domain name of at most 253 characters, internationalized names in their xn-- form. Case and a trailing dot are ignored. An address, a URL or a wildcard is refused (domains.invalid).

NewDomainConnectAuthorization

FieldTypeDescription
accept_mx_changeboolean

The person agreed that the domain's MX records are replaced and its incoming mail moves to Box. Needed when requires_mx_acknowledgment is true; set it only with the person's agreement.

default false

stagestring: ownership, mail or null

The stage the caller expects (stage of the domain's availability); refused (domain_connect.stage_changed) if the domain has moved on. Omit it for the current stage.

default null

NewDraft

FieldTypeDescription
attachmentsarray of KeptDraftAttachment or OutgoingAttachment

The draft's own files, in order, at most 10 and 10 MiB in all: each either a new file (filename, content_type, content_base64, optional charset) or {"id": "att_..."}, one the draft already has, kept as it is without sending its content again. A file of the draft left out is removed. A forward's message's own attachments are not here (original_attachment_ids).

max items 10

bccarray of string (email)

Blind copies: addresses the message will be sent to without any recipient seeing them. At most 50 in each field and 100 in all.

max items 50

ccarray of string (email)

The draft's Cc; as to (for a reply, [] is no Cc, leaving it out keeps Box's).

max items 50

include_original_attachmentsboolean

Only for a forward: whether the forwarded message's own attachments go with it when it is sent. false forwards its text only.

default true

kindstring: new, reply, reply_all, forward

What the draft is: a message of its own (new), or a reply, a reply_all or a forward of the message message_id. It never changes.

default "new"

message_idstring

For a reply, a reply to all or a forward: the message (msg_...) of this mailbox it answers or forwards. Reading it needs messages:read.

min length 1 · max length 64

original_attachment_idsarray of string

Only for a forward: which of the forwarded message's own attachments go, by the id its read lists each with (att_...), each once. [] sends none. Leave it out to send them all. Not with include_original_attachments: false.

max items 10

subjectstring

The subject. For a reply or a forward, leave it out to keep Box's (Re: or Fwd: and the message's); for a new message, leaving it out is "".

max length 998

textstring

The plain-text body, as it will be sent. For a forward, the note above the forwarded message, which Box adds when it is sent.

max length 1000000 · default ""

toarray of string (email)

The draft's To. A draft may be incomplete: no recipient yet is fine, and duplicates are checked when it is sent. For a reply, leave it out to keep Box's (who the reply would go to, worked out from the message when it is sent); for any other draft, leaving it out is [].

max items 50

NewMailbox

FieldTypeDescription
domain_id requiredstring

The domain the mailbox is on (dom_...), one of the account's domains. It must be active.

min length 1 · max length 64

local_part requiredstring

The part of the address before the @: 1 to 64 lower-case letters, digits, ., _ and -, starting and ending with a letter or digit, without ... Upper case is accepted and stored lower-cased. Names reserved for the mail system, such as postmaster or abuse, are refused.

NewWebhook

FieldTypeDescription
url requiredstring

Where to deliver events: an https URL on port 443 or 1024-65535, without user name or password, whose host is a public DNS name or address. See createWebhook.

max length 2048

OutgoingAttachment

FieldTypeDescription
charsetstring or null

Only for a text/ content_type: the character set the content's bytes are in, such as utf-8, utf-16 or iso-8859-1, sent as the attachment's charset (lower-cased). Box does not convert the content: name the character set it is already in. Omitted, none is stated, and recipients read the text as US-ASCII.

max length 40 · default null

content_base64 requiredstring

The content, base64-encoded (standard alphabet, padded, no line breaks).

content_typestring

The attachment's media type, such as application/pdf.

max length 127 · default "application/octet-stream"

filename requiredstring

The file name recipients see.

min length 1 · max length 255

OutgoingMessage

FieldTypeDescription
attachmentsarray of OutgoingAttachment

Files to attach, at most 10 MiB in total.

max items 10

bccarray of string (email)

Addresses to send a blind copy to: they get the message, and no recipient sees them (only the mailbox's own copy shows them). Each recipient may appear only once across to, cc and bcc, at most 100 in all.

max items 50

ccarray of string (email)

Addresses to copy. Each recipient may appear only once across to, cc and bcc.

max items 50

subject requiredstring

max length 998

text requiredstring

The plain-text body.

max length 1000000

to requiredarray of string (email)

Recipient addresses.

min items 1 · max items 50

PlanCancellation

FieldTypeDescription
effective_at requiredstring (date-time) or null

When the move takes effect, or took effect (downgraded).

status requiredstring: cancellation_scheduled, awaiting_effective_time, downgraded

cancellation_scheduled: the plan ends at effective_at, the end of the period already paid for, and is not renewed; the account keeps its plan, in full, until then, and moves to target_plan then if it fits it (otherwise it waits, held back: getBilling). awaiting_effective_time: the payment already ended, before effective_at; the account keeps its plan (held back) until then, and moves when the owner cancels again at or after it. downgraded: the account is on target_plan now.

target_plan required"free"

The plan the account moves to.

PlanEligibility

FieldTypeDescription
blockers requiredobject of PlanFit

Each resource over the target's limit, by name (mailboxes, domains, storage_bytes), and by how much. Empty when the account fits. The monthly email count never blocks a move.

current_plan requiredstring

The account's plan now.

domains requiredPlanFit

Domains, counted as for the domain limit, against the target's.

downgrade requiredboolean

The target allows fewer mailboxes, fewer domains or less storage. Only a downgrade can be blocked; a move to a larger plan, or to the current one, always fits.

eligible requiredboolean

The account fits the target plan now: nothing blocks the move. Usage can change before the move is made, which checks it again.

mailboxes requiredPlanFit

Mailboxes, counted as for the mailbox limit, against the target's.

storage requiredStorageFreshness or null

For a move to a plan with less storage: how current storage_bytes.used is. Null for any other move, which does not depend on stored mail: storage_bytes.used is then the figure as last measured.

storage_bytes requiredPlanFit

Stored mail in bytes, against the target's storage limit: for a move to less storage, measured for this check (storage).

target_plan requiredstring: free, starter

The plan checked.

PlanFit

FieldTypeDescription
excess requiredinteger

How far used is over limit; 0 when it fits.

limit requiredinteger

The target plan's limit.

used requiredinteger

What the account has now.

PlanRenewal

FieldTypeDescription
renews_at requiredstring (date-time) or null

When the plan renews next (the end of the current paid period); null when unknown.

status required"renewing"

renewing: the paid plan renews automatically at renews_at, the end of the period already paid for, and its recurring payment is charged then, as before the cancellation; nothing was charged now. The owner's cancellation of it is taken back.

RecipientDelivery

FieldTypeDescription
address requiredstring

The recipient's address, lower-cased: the data.recipient of its events.

reason requiredstring: recipient_address, recipient_mailbox, mail_system, network, protocol, content, policy, refused_temporarily, other or null

For deferred and failed: the category of status (recipient_address is no such address, recipient_mailbox for example full or disabled; refused_temporarily: the mail system refused this recipient for now when the message was submitted, and did not retry; Box did not deliver to it, but the address is not known to be bad, and sending to it again later may deliver it). Otherwise null.

state requiredstring: pending, deferred, delivered, failed, canceled

What has become of the message for this recipient now. pending: no outcome known yet. deferred: delivery failed temporarily and is being retried. delivered: the recipient's mail system accepted the message. failed: delivery failed and is not retried (with reason refused_temporarily, only for now). canceled: the mail service stopped the message before it reached this recipient (sending was suspended for abuse) and it will not be sent. delivered, failed and canceled are final.

status requiredstring or null

For deferred and failed: the mail status code, such as 5.1.1, if there is one. Otherwise null.

RefusedRecipient

FieldTypeDescription
address requiredstring

The recipient's address, lower-cased: the data.recipient of its message.delivery_failed event.

reason requiredstring: recipient_address, recipient_mailbox, mail_system, network, protocol, content, policy, refused_temporarily, other

Its category, as for a recipient of the message: refused_temporarily: the mail system refused this recipient for now when the message was submitted, and did not retry; Box did not deliver to it, but the address is not known to be bad, and sending to it again later may deliver it.

status requiredstring or null

The mail status code of the refusal, such as 4.4.5, if there is one.

ReplyMessage

FieldTypeDescription
attachmentsarray of OutgoingAttachment

Files to attach, at most 10 MiB in total.

max items 10

bccarray of string (email)

Addresses to send a blind copy to: they get the message, and no recipient sees them (only the mailbox's own copy shows them). Each recipient may appear only once across to, cc and bcc, at most 100 in all.

max items 50

ccarray of string (email)

The reply's Cc, instead of Box's (none for a reply; the message's Cc for a reply to all). [] sends no Cc. Leave it out to keep Box's.

max items 50

subjectstring

The reply's subject, instead of Re: and the message's. The reply still names the message (In-Reply-To, References), but a changed subject starts a conversation of its own in the mailbox. Leave it out to keep Box's.

max length 998

text requiredstring

The reply's plain-text body, exactly as it is sent: the message replied to is not quoted in it.

max length 1000000

toarray of string (email)

The reply's To, instead of the one Box works out from the message. Leave it out to keep Box's.

min items 1 · max items 50

RequestingClient

FieldTypeDescription
id requiredstring

The client's OAuth client id.

name requiredstring or null

The name the client registered with, if any. Chosen by the client.

uri requiredstring or null

The web page the client registered, if any. Chosen by the client.

ResourceQuota

FieldTypeDescription
limit requiredinteger

The plan's limit.

remaining requiredinteger

How many more the plan allows: limit minus used, and 0 (never negative) when used is at or over it.

used requiredinteger

How many the account has now. Can be more than limit (after a move to a smaller plan, for example); it is never cut to the limit.

RestoredMessage

FieldTypeDescription
folder requiredstring: inbox, sent, junk

Where the message is now, and in no other folder: inbox for mail the mailbox received, sent for mail it sent, junk for a message held in Junk, which restoring never releases.

from requiredstring or null

The sender's address, if the message names one.

from_name requiredstring or null

(display names) The name the message's From field gives with from, as the message says it: for mail the mailbox sent, the name it was sent with; for any other mail, what its sender wrote, which nothing verifies (anyone can write any name). Control and bidirectional formatting characters are shown as spaces, white space as one space, at most 256 characters. Null if it gives none.

has_attachments requiredboolean
id requiredstring

The message's Box id: the message_id of its events.

is_read requiredboolean

Whether the message is marked as read. New mail arrives unread; a message the mailbox sent is read. Change it with updateMessage.

junk requiredJunkDetails or null

For a message restored to Junk: why it is held and how long it is kept. Restoring gives it no new period: it may be deleted as it would have been in the trash (expires_at). Null otherwise.

mailbox_id requiredstring

The mailbox the message is in.

preview requiredstring

The start of the message's text, for display in a list.

received_at requiredstring (date-time)

When the message arrived in the mailbox (for mail the mailbox sent: when it was sent).

size requiredinteger

Size of the whole message in bytes.

subject requiredstring or null
thread_id requiredstring or null

The thread (thr_...) the message is in: the conversation of the messages it answers and that answer it, as getThread reads it, the mail the mailbox sent included. Null for a message not placed in one yet.

to requiredarray of string

The recipient addresses the message names in To.

StorageFreshness

FieldTypeDescription
fresh requiredboolean

true: storage_bytes.used was measured for this check, every active mailbox of the account at or after since. false: it is the figure as last measured before, which never blocks the move and never makes it eligible: it is shown only beside a mailbox or domain blocker, which decides the answer anyway.

mailboxes requiredinteger

The active mailboxes the storage figure is the total of.

measured_at requiredstring (date-time) or null

The oldest measurement in storage_bytes.used. Null when the account has no mailbox, or has one not measured yet.

since requiredstring (date-time)

When the check began.

StorageQuota

FieldTypeDescription
limit_bytes requiredinteger

The plan's storage limit, in bytes.

measured_at requiredstring (date-time) or null

How current used_bytes is: the oldest of the latest measurements of the account's mailboxes, since the total is only as current as its least recent part. Mail that arrived since then is not counted yet. Null when the account has no mailbox, or has one not measured yet. Checking plan eligibility measures storage now.

remaining_bytes requiredinteger

limit_bytes minus used_bytes, and 0 (never negative) when used_bytes is at or over it.

used_bytes requiredinteger

Mail stored in all of the account's mailboxes, in bytes (1 GB is 1,000,000,000 bytes), as last measured. Can be more than limit (after a move to a smaller plan, for example); it is never cut to the limit.

SubmittedMessage

FieldTypeDescription
id requiredstring

The sent message's Box id. Every retry of the send returns it, and delivery events name the message by it (message_id). The mailbox's copy of what was sent, when it keeps one, is read by the same id; do not rely on that copy being there, or its content being readable, as soon as the send returns.

mailbox_id requiredstring

The mailbox that sent it.

refusedarray of RefusedRecipient

Present only when the mail system refused some recipients when the message was submitted: those recipients. The message was not sent to them, nothing retries them, and retrying with the same Idempotency-Key returns this same response without sending anything; to try them again, send to them with a new key. Each also has a message.delivery_failed event. The recipients not listed were accepted for delivery, which does not mean they were delivered: follow the event feed.

status required"submitted"

submitted: Box has accepted the message for delivery. Delivery to the recipients happens afterwards, and is reported on the event feed.

Thread

FieldTypeDescription
id requiredstring

The thread's id (thr_...). A thread merged into another since its id was handed out is read under the id of the thread it became.

mailbox_id requiredstring

The mailbox the thread is in.

messages requiredarray of MessageDetail

Its messages, oldest first: at most 100, the newest ones.

omitted requiredinteger

How many older messages are left out of messages (0 when none are). Each is still readable with getMessage.

subject requiredstring or null

The subject of the thread's first message shown.

ThreadPage

FieldTypeDescription
data requiredarray of ThreadSummary

Threads, the one with the newest message first; at most limit.

next_cursor requiredstring or null

Pass as cursor for the next page; null on the last page.

ThreadSummary

FieldTypeDescription
folders requiredarray of string: inbox, sent, drafts, trash, junk, archive, other

The folders those messages are in now, each once.

has_attachments requiredboolean

Whether any of those has attachments.

id requiredstring

The thread's id (thr_...): getThread reads its messages.

last_message_at requiredstring (date-time)

When the newest of those arrived (for mail the mailbox sent: when it was sent).

latest requiredMessage

The newest of those messages, as listMessages shows a message.

mailbox_id requiredstring

The mailbox the thread is in.

message_count requiredinteger

How many of its messages the listing shows (message_ids).

message_ids requiredarray of string

The ids of those messages, oldest first.

participants requiredarray of string

The senders of those messages, oldest first, each once.

subject requiredstring or null

The subject of the thread's first message shown.

unread_count requiredinteger

How many of those are unread.

Webhook

FieldTypeDescription
created_at requiredstring (date-time)
event_cursor requiredstring

Pass as after to GET /v1/events to read the events this webhook has not delivered yet, in the order it delivers them. For a new webhook: the events after the ones on the feed when it was created.

failing_since requiredstring (date-time) or null

When attempts started failing, while they keep failing; null after a successful delivery or before any failed one.

id requiredstring

The webhook's Box id.

last_delivered_at requiredstring (date-time) or null

When an event was last delivered to it; null before the first delivery.

state requiredstring: active, failed

active: Box delivers events to it, retrying failed attempts. failed: every attempt failed for 3 days, and Box no longer delivers to it. The events it did not deliver are on GET /v1/events after event_cursor. Delete it and create a new webhook to resume.

url requiredstring

Where Box delivers the events.

WebhookList

FieldTypeDescription
data requiredarray of Webhook

The customer's webhooks, oldest first.