NewThree filters returned the wrong rows
BeeL
Get StartedMulti-NIFVeriFactuStripeAPI ReferenceChangelog

Sending Email

How BeeL. delivers invoice emails — the sandbox recipient restriction, per-account send quotas, queued delivery, and bulk limits.


Sending an invoice by email is POST /v1/companies/{company_id}/invoices/{invoice_id}/send. Two things about it are not obvious from the endpoint reference, and both will bite an integration: the sandbox only lets you write to yourself, and there are send quotas that are separate from the API rate limits.

Sandbox only sends to your own address

With a beel_sk_test_ key you can only send to the account holder's own address. Any other recipient is refused with 403 ENVIO_NO_PERMITIDO.

Plus-addressing is fully supported, and it is the intended way to separate test sends. The comparison strips the +tag before matching, so all of these count as your address:

you@yourdomain.com
you+test1@yourdomain.com
you+customer-fixture@yourdomain.com

The rule applies to every recipient, to and cc.

In production this rule does not apply: you send to whoever the invoice is addressed to. The 403 is specific to test keys.

Send quotas

Separate from the API rate limits, which count HTTP requests, there are quotas on emails actually sent, per account. Note the unit: one bulk call with 40 recipients is one request but forty emails.

AxisProductionSandbox
Emails per hour6010
Emails per 24 h30030
Distinct recipient addresses per 24 h100100
Sends of the same invoice per 24 h1010

The windows are rolling, not calendar. Each one starts at your first send and lasts 24 hours (or one hour) from there — there is no reset at midnight. Send at 23:00 and resume at 09:00 and you are still inside the same window.

The distinct-recipients axis counts how many different addresses you wrote to, not how many emails you sent: ten invoices to the same customer count as one address.

Every email your account sends counts, not just invoices. Account verification, password resets, invitations and welcome messages come out of the same allowance, because they are attributed to your account too. Only mail with no account behind it at all is exempt. In practice this is a handful a month, but it is the same bucket.

Spam complaints reduce these ceilings — each complaint in the last 30 days halves them, and at three the account is suspended from sending. Ordinary invoicing never approaches this.

Need more? These are defaults, not a hard ceiling. If your billing volume needs higher limits, ask us at it@beel.es and tell us your monthly volume and your peak day — the link opens a message with the fields we need.

What you get back

StatusWhen
429Quota exhausted on some axis. There is a Retry-After — it will work later.
403Not a quota problem: the destination itself is not allowed. Waiting changes nothing.

The response does not name which axis you hit or how much is left, so program against the status code rather than trying to read a reason out of the body.

Where the rejection reaches you

Both checks run synchronously when you ask for the send — /send and /deliveries. There you get the 429 or 403 as the response, and nothing was queued.

An auto-sent invoice is different. When you create an invoice with send_automatically, the creation succeeds and returns 201; the email is authorised later, when it is delivered. So a quota or destination rejection does not reach you as an error on that call — it lands in the delivery history as a failed send. If you rely on automatic sending, read the outcome there rather than from the creation response.

Sending a whole month at once

Plenty of integrations bill on the last day of the month and send everything in one run. That is the run the quotas bind on first, so it is worth pacing rather than firing in a loop.

Work out which limit you hit first. For a run of N invoices to N different customers, in production:

Customers in the runWhat stops you
Up to 60Nothing, if you pace it across the hour
60–100The hourly cap — spread the run out
Over 100The distinct-recipients cap — the run does not fit in one window

The 300-email cap is rarely the binding one. Distinct recipients (100 per 24 h) is, because a month-end run is by definition one email each to many different people.

How to run it

  1. Pace to roughly one email per minute. That keeps you under 60/hour without ever seeing a 429.
  2. Handle the 429 as a pause, not a failure. It carries Retry-After in seconds — sleep that long and continue from the same invoice. Nothing was sent, so there is nothing to undo.
  3. Split a run of more than 100 customers, and mind where the window starts. The 24 hours run from your first send, not from midnight: if you send 100 at 23:00, you are not clear again until 23:00 the next day, not at 00:00. Plan the batches from your own start time. Issuing the invoices is not limited — only sending them is, so you can create all 250 on the last day and drain the sending queue over the following days. The 429 carries Retry-After with the exact seconds left, so the safest loop is to honour it rather than compute the window yourself.
  4. Ask for more instead of engineering around it if this is your normal shape. Tell us your peak day using the link above and we will raise the limits.

Where bulk delivery helps, and where it does not

POST /v1/companies/{company_id}/invoices/deliveries puts many invoices in one email, so it costs one email against your quota no matter how many invoices it carries.

That helps when several invoices go to the same recipient — a customer with twelve monthly invoices, or a copy of everything to your own accountant. It does not help a month-end run to many different customers: each customer still needs their own email, so you still spend one email and one distinct recipient per customer.

Delivery itself is asynchronous

Once the send is authorised, the actual delivery is queued. A 2xx therefore means accepted for delivery, not delivered:

  • sent_at in the response is when BeeL. queued the email. On the invoice resource, sent_at is when the provider accepted the message — still not when it reached the mailbox.
  • email_id in the /send response is not the provider's message id.
  • A send can still fail after the 2xx — a bounce or a provider outage surfaces later.

Observing what actually happened

SourceWhat it gives you
GET /v1/accounts/{account_id}/emailsDelivery history: one record per email with status, recipients and timestamps
GET /v1/accounts/{account_id}/email-indicatorsAggregate counters for the account
The invoice's sending_historyEvery send of that invoice — a resend appends, it never overwrites
The invoice.email.sent webhookFires on a successful send — see Events

Statuses are SENT, FAILED, DELIVERED, BOUNCED and OPENED.

Retries

A failed delivery is retried with exponential backoff and jitter, up to 3 attempts, then dead-lettered — nothing retries it for you after that. Failures that cannot improve on a retry (an invalid recipient, a rejected destination) are dead-lettered on the first attempt instead of burning all three.

Recipients

The recipient list is resolved in this order, first match wins:

  1. recipients in the /send body
  2. The account's email defaults, if configured
  3. The invoice's own email_config
  4. The customer's billing emails

If nothing resolves, the call fails with 400. A draft invoice cannot be sent — issue it first.

Reserved domains never send

Addresses on the reserved documentation domains and non-routable TLDs of RFC 2606 and RFC 6761 are dropped before the provider is called, because it would reject them permanently anyway:

RejectedValues
Domainsexample.com, example.net, example.org (and any subdomain)
TLDs.test, .example, .invalid, .localhost, .local

A fixture customer with cliente@example.com produces a FAILED record and no email. Use a +tag on your own address instead — which is what the sandbox requires anyway.

Bulk sending

POST /v1/companies/{company_id}/invoices/deliveries sends the PDFs of several invoices as one email to the recipients you name. It is not one email per customer, and at least one explicit recipient is required — nothing is inferred from the invoices.

LimitValue
Max invoices per request200
PDFs attached individuallyUp to 5
PDFs bundled as a single ZIP6 or more
ZIP download link lifetime24 hours

Invoices must be ISSUED, PAID, SENT or OVERDUE. Partial success is normal: if some PDFs cannot be attached the email still goes out with the rest and the response carries a failures array, so a 2xx does not mean all of them made it in.

Because it is one email, a 200-invoice bulk send costs one email against your quota, not 200 — see Sending a whole month at once for when that actually helps.

Checklist

  • In sandbox, send only to your own address — use +tag to separate test runs
  • Treat 403 ENVIO_NO_PERMITIDO as "change the recipient", never as "retry"
  • Treat 429 on /send as a send quota, not an API rate limit
  • Treat a 2xx as "queued", and read the delivery history for the real outcome
  • Never put example.com or a .test address in fixtures
  • Pace month-end runs at ~1 email/minute, and split them if they exceed 100 customers
  • Remember the windows roll from your first send, not from midnight
  • Treat an auto-sent invoice's 201 as "created", not as "email accepted"
  • Use bulk delivery when several invoices share a recipient — not as a way around the caps
  • Write to it@beel.es if your volume needs higher limits