Bee Issuance API
Credit semillas (community vouchers) to a member from an external system.
Base URL: https://bee.redbroomsoftware.com
Which side of Bee are you on?
This page is for systems that give value to a member — a recycling drop-off point, a sponsor-funded rewards program, any process that decides "this person earned X".
If instead you are a merchant or POS that wants to let a customer pay part of a ticket with vouchers, you want the Bee app page and /api/charge-requests. The two directions are separate endpoints with separate rules.
Before you write code
Read what a semilla is first. Two rules from that page decide how this API behaves, and neither is negotiable:
- A voucher is a liability of the issuing cell, never of the community administration. When you credit value, you are creating a liability in some cell's name — so the API always requires knowing which cell answers for it.
- Minted value cannot be un-minted. There is no reversal endpoint and there will not be one. This is why
Idempotency-Keyis mandatory rather than recommended.
Authentication
X-API-Key: <your key>
Content-Type: application/jsonKeys live in Bee's database, not in environment variables. Each key carries:
- a community — the tenant you may write to,
- a cell scope — the specific cells you may issue on behalf of,
- two independent revocation switches.
Keys are revoked without a deployment, effective on the next request. Ask the community administration to issue yours; they are created deliberately, one per integrator.
Treat 401 as a loud failure
Do not log a 401 and move on. A previous integration in this ecosystem compared the key against an environment variable while the caller sent a database key: every real request got 401, the sender wrote it to a log nobody read, and the channel was silently dead for months. A 401 here means nothing was credited. Alert on it.
Credit value for recycling
POST /api/reciclajeHeaders
| Header | Required | Description |
|---|---|---|
X-API-Key | yes | Your key |
Idempotency-Key | yes | Stable, unique id for the real-world event — the drop-off, the weighing, the transaction in your system. Not a random per-request value. |
Body
| Field | Type | Required | Description |
|---|---|---|---|
phone_e164 | string | yes | The member's phone. E.164 (+5215512345678); Mexican legacy forms are normalized for you. This is the identity in Bee — there are no usernames. |
amount_cents | integer | yes | Value to credit, in whole cents. 1 seed = 1 MXN, so 25000 = $250.00 MXN. Never a float. |
issuer_cell_id | string | only if your key covers more than one cell | The cell that answers for this liability. With a single-cell key it is inferred. |
source_ref | string | no | Your id for the event. Echoed back; useful for reconciliation. |
destino | string | no | The community's budget slug this comes out of. Must be one the community declared. |
You send the amount, not the raw measurement. Bee does not convert kilograms to seeds — your conversion model is yours. Bee validates, mints and records.
Example
curl -X POST https://bee.redbroomsoftware.com/api/reciclaje \
-H "X-API-Key: $BEE_API_KEY" \
-H "Idempotency-Key: dropoff-2026-09-23-00417" \
-H "Content-Type: application/json" \
-d '{
"phone_e164": "+5215512345678",
"amount_cents": 25000,
"source_ref": "dropoff-00417"
}'{
"ok": true,
"batchId": "…",
"amountCents": 25000,
"memberId": "…",
"issuerCellId": "…",
"duplicate": false,
"expiresAt": "2027-09-23T…",
"sourceRef": "dropoff-00417"
}Two response fields that are not decoration
duplicate: true means this event was already credited. It is success, not an error — the idempotency worked. Do not retry it and do not add it to the balance a second time.
expiresAt is when this value dies. Vouchers expire (one year by default, anchored to the batch at issuance — a voucher does not get younger because its issuer minted a new batch). Put this date on whatever receipt you hand the person. Promising value without saying when it expires is your app lying on Bee's behalf.
Errors
| Status | error | What it means and what to do |
|---|---|---|
| 400 | idempotency_key_required | You omitted the header. Nothing was credited. |
| 400 | invalid_phone | Not a parseable phone number. |
| 400 | invalid_amount | Not a positive whole number of cents. Floats are rejected. |
| 401 | unauthorized | Key missing, unknown, inactive or revoked. Deliberately indistinguishable — alert, do not swallow. |
| 403 | cell_out_of_scope | That cell is not in your key's scope. You cannot create a liability in another cell's name. |
| 404 | member_not_found | The most common one in the field. That phone is not in the community's roster. |
| 409 | quota_exceeded | The issuing cell has no monthly issuance quota (polen) left. |
| 422 | issuer_cell_required | Your key covers several cells; say which one. Bee will not choose for you. |
| 422 | amount_too_large | Above the per-operation ceiling. A safety belt against a unit mistake — cents read as pesos are 100×. |
Handling member_not_found
Bee will not create the person for you. Identity is the phone number, and enrolling someone from an integrator's key would turn this endpoint into a public sign-up path.
Show it at the counter as what it is: "this phone isn't registered with the community yet", and let the community enroll them. In practice the beneficiaries are recurring organizations, so this is a one-time step, not daily friction.
How the value behaves once credited
Worth knowing, because it shapes what the person can do with it:
- Vouchers from the cell where you are paying cover 100% of a ticket. Vouchers from other cells are capped — by default 30% of the ticket, aggregated.
- A cell can be marked exempt from that cap by the community, and a drop-off point normally is: its liability is backed by money sponsors already paid, so its value spends at full weight anywhere in the community while still being recorded as owed by that cell.
- The acceptance percentage is resolved per issuing cell. It is expected to become richer over time (by member type and by venue type), so do not hard-code 30% anywhere in your integration — read what Bee returns.
Idempotency, concretely
See the general idempotency guide. The one rule specific to this endpoint:
Key the request on the real-world event, not the attempt.
A retry after a timeout must carry the same key as the original — the first call may well have succeeded and only the response was lost. Two different keys for one drop-off are two real credits, and there is no way back.