POST /v1/organizations/:orgId/credits/allocate
Fund a child organization from your parent wallet - idempotent money movement.
/v1/organizations/{orgId}/credits/allocate- Auth
- Bearer
- Scope
- org:admin
To put credits into a customer's wallet, allocate from your parent wallet to the child. You call this with your parent (org:admin) key. The credits move out of your prepaid balance and into the child's; the response reports how much moved and the child's post-allocation balances, so you don't need a follow-up read.
This is real money movement - make it idempotent. The transfer is recorded on both ledgers: a debit on yours, a positive allocation event on the child's (see child credit events).
orgIdstring (org_…)requiredThe child organization to fund. Must be a direct child of your org.
creditsintegerrequiredHow many credits to move from your wallet to the child. Must be a positive integer (> 0). A value ≤ 0 is rejected with 422.descriptionstringoptionalOptional free-text note for this transfer (e.g. an invoice number or budget label), ≤ 500 chars. Recorded on the allocation event on both ledgers.metadataobjectoptionalOptional opaque key/value pairs for your own reconciliation (Stripe-style). Merged onto the allocation ledger event and readable via GET …/credits/events. Layers never reads or indexes it; reserved system keys (direction, counterpartyOrgId, etc.) always take precedence.
Idempotency-Keystring (UUID)requiredREQUIRED on this money-movement route — a request without it is rejected with 400 IDEMPOTENCY_REQUIRED. A replay (same key + same body) returns the original result WITHOUT re-debiting your wallet; the key is enforced at both the HTTP layer and the database transaction. Reusing a key with a different body returns 409 IDEMPOTENCY_CONFLICT.
Example
curl -X POST https://api.layers.com/v1/organizations/org_d4e5f6a7-8b9c-4d0e-9f2a-3b4c5d6e7f80/credits/allocate \
-H "Authorization: Bearer $LAYERS_PARENT_KEY" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{
"credits": 5000,
"description": "Q3 budget top-up",
"metadata": { "invoice": "inv_2026_0142" }
}'description and metadata are optional. Both are attached to the allocation event on each side of the transfer, so you can reconcile a movement later from GET …/credits/events without keeping your own mapping.
{
"id": "txn_7f3a2b1c-9d8e-4f0a-b1c2-3d4e5f6a7b8c",
"organizationId": "org_d4e5f6a7-8b9c-4d0e-9f2a-3b4c5d6e7f80",
"allocated": 5000,
"balance": 5000,
"available": 5000,
"description": "Q3 budget top-up",
"metadata": { "invoice": "inv_2026_0142" },
"created": "2026-06-03T18:14:02.187000+00:00"
}Field notes
idis the transfer id (txn_-prefixed) — the parent-ledger row this allocation wrote. It's your stable reconciliation handle and matches thetransferIdon theallocationevent on both ledgers; re-fetch it via GET …/credits/events.allocatedechoes the amount that moved. On an idempotent replay it's the originally-allocated amount, not a second transfer.balanceandavailableare the child's balances after the allocation - the same two numbers GET …/credits returns.availableis net of the child's reserved (in-flight) credits, so it can be belowbalance.descriptionandmetadataare echoed back from the request (metadatadefaults to{}when omitted).createdis the transfer timestamp.- Funding a suspended child is allowed - you can pre-fund a paused customer ahead of resuming them. Funding an archived child is not (see errors).
Idempotency
Always send an Idempotency-Key. This call debits your wallet, so a blind retry after a timeout would fund the child twice. With the key, a replay returns the original result and moves nothing - the guard is enforced both at the HTTP layer and inside the database transaction. See common patterns → idempotency.
Errors
| Status | Code | When |
|---|---|---|
| 400 | IDEMPOTENCY_REQUIRED | No Idempotency-Key header — it's mandatory on this money-movement route. |
| 402 | BILLING_EXHAUSTED | Your parent wallet doesn't have enough balance to cover the requested credits. Top up, then retry. |
| 404 | NOT_FOUND | :orgId is not a direct child of your org (anti-enumeration). |
| 409 | CONFLICT | The child is archived - a terminal org accepts no funding (its credits were reclaimed at archive). |
| 409 | IDEMPOTENCY_CONFLICT | Same Idempotency-Key reused with a different request body. |
| 422 | VALIDATION | credits is ≤ 0 or non-integer, the body is malformed, or :orgId is malformed. |
| 503 | KILL_SWITCH | Your key or org is suspended. |
Archiving reclaims unspent credits
When you archive a child (DELETE /v1/organizations/:orgId), its unspent, non-reserved credits are reclaimed to your parent wallet, and the archive response reports the amount as reclaimedCredits. Archive is terminal: once archived, a child can't be funded again - the allocate call above returns 409 CONFLICT. Reserved credits backing in-flight work are not reclaimed mid-flight.
See also
- Child wallet - confirm the new balance.
- Child credit events - the
allocationevent this creates. - GET /v1/credits - your own wallet, debited by each allocation.