POST /v1/bulk-deliveries/{ref}/recipients/{child_ref}/remove
POST /v1/bulk-deliveries/{ref}/recipients/{child_ref}/remove
| Scope | Idempotency-Key | Operation ID |
|---|---|---|
delivery.create | Required | removeBulkRecipient |
Remove one recipient before the sender confirms.
Before the sender confirms, marks one never-dispatched child REMOVED: it keeps its position
and history (at most five children ever), its link stops working, and the parent's counters
and status are derived again from its children. A quoted total no longer matches
and is withdrawn; the quote revision moves on. The last active recipient cannot be
removed (BULK_STATE_CHANGED, detail.reason last_recipient): cancel the Bulk instead.
Removing a removed child changes nothing and returns the Bulk. Idempotency-Key
required. Audited (bulk.recipient.remove).
Request
Signed with your API credential: see Authentication.
| Parameter | In | Required | Description |
|---|---|---|---|
Idempotency-Key | header | Yes | One key per user intent, reused for every retry of that intent. See Idempotency. |
ref | path | Yes | Pattern ^BK-[0-9A-HJKMNP-TV-Z]{8}$. |
child_ref | path | Yes | Public reference of a delivery job or Bulk child. Pattern ^DX-[0-9A-HJKMNP-TV-Z]{8}$. |
Responses
200
Removed (or already removed).
| Field | Type | Required | Description |
|---|---|---|---|
bulk_reference | string | Yes | Pattern ^BK-[0-9A-HJKMNP-TV-Z]{8}$. |
status | string | Yes | One of: BULK_DRAFT, BULK_AWAITING_RECIPIENTS, BULK_PARTIALLY_CONFIRMED, BULK_READY_FOR_QUOTE, BULK_AWAITING_SENDER_CONFIRMATION, BULK_DISPATCHING, BULK_IN_PROGRESS, BULK_PARTIALLY_COMPLETED, BULK_COMPLETED, BULK_CANCELLED, BULK_EXCEPTION. |
recipient_count | integer | Yes | 2–5. |
active_recipient_count | integer | Yes | 0–5. |
confirmed_count | integer | Yes | 0–5. |
quote_revision | integer | Yes | |
total | string or null | Yes | The sum of the children's quotes, null until every active child is quoted. |
currency | string | Yes | One of: GHS. |
children | array of object | Yes | |
children[].position | integer | Yes | 1–5. |
children[].delivery_reference | string | Yes | Public reference of a delivery job or Bulk child. Pattern ^DX-[0-9A-HJKMNP-TV-Z]{8}$. |
children[].status | string | Yes | The child request's status (request-level states; REMOVED once removed) |
children[].location_confirmed | boolean | Yes | The recipient confirmed the drop-off |
children[].recipient | object | Yes | |
children[].recipient.first_name | string | Yes | |
children[].recipient.last_name | string | Yes | |
children[].recipient.phone | string | Yes | Masked: the last three digits only |
children[].link_expires_at | string or null, date-time | Yes | When the child's live link stops working; null once revoked |
children[].resends_remaining | integer or null | Yes | Resends left while the recipient is awaited, else null |
children[].resend_available_at | string or null, date-time | Yes | When the resend cooldown ends while the recipient is awaited, else null |
children[].quote | object or null | Yes | The child's own quote: the server's total only, never its arithmetic. expired is judged by the server clock. Null before the child is quoted. |
children[].quote.reference | string | Yes | Pattern ^QT-[0-9A-HJKMNP-TV-Z]{8}$. |
children[].quote.total | string | Yes | Exact decimal with two places; never a JSON number. Pattern ^-?[0-9]+\.[0-9]{2}$. |
children[].quote.currency | string | Yes | One of: GHS. |
children[].quote.expires_at | string, date-time | Yes | |
children[].quote.expired | boolean | Yes | |
children[].customer_status | string or null | Yes | The child's delivery status as tracking shows it, once dispatched |
children[].tracking_links | object | No | The child's tracking links, shown once on the tenant's confirming response: the sender's link confirms the handover |
children[].tracking_links.sender_url | string | Yes | |
children[].tracking_links.recipient_url | string | Yes | |
children[].tracking_links.expires_at | string, date-time | Yes | |
cancellations | array of object | No | After dispatch, each child's answer to the cancellation; present only on that answer. |
cancellations[].delivery_reference | string | Yes | Public reference of a delivery job or Bulk child. Pattern ^DX-[0-9A-HJKMNP-TV-Z]{8}$. |
cancellations[].cancelled | boolean | Yes | |
cancellations[].reason | string or null | Yes | Why the stage policy left it, e.g. after_pickup |
created_at | string, date-time | Yes |
400
Structural validation failed or an Idempotency-Key is missing.
The error envelope: code, message, detail, request_id.
401
Unknown, revoked or wrong-environment credential, bad signature, or expired session.
The error envelope: code, message, detail, request_id.
403
Tenant access denied (existence is not disclosed), insufficient scope, MFA/step-up required, partner blocked or module disabled.
The error envelope: code, message, detail, request_id.
409
State changed, race lost, or idempotency conflict. Refetch before deciding again.
The error envelope: code, message, detail, request_id.
422
Semantically invalid (location, vehicle, server-controlled field, bulk limits).
The error envelope: code, message, detail, request_id.
429
Rate limit reached; honour Retry-After.
The error envelope: code, message, detail, request_id.
500
Unexpected failure. Only code and request_id are returned.
The error envelope: code, message, detail, request_id.
503
Pricing/routing unavailable, request timeout, retryable contention, or an operator must act first (ACCOUNTING_PERIOD_UNAVAILABLE, outcome REQUIRES_ADMIN), or the payment provider cannot start a payment (PAYMENT_PROVIDER_UNAVAILABLE, outcome REQUIRES_PAYMENT; nothing charged).
The error envelope: code, message, detail, request_id.