Close a campaign and recover unused credit
Permanently stop delivery, track final costs, and see unused wallet credit returned.
Pause or finish
Pause when you may restart. Close when the campaign is finished. Closing permanently prevents campaign resume, ad resume and budget changes. It uses the existing campaigns:write permission and cannot increase spending. A live campaign requires a live key.
Read GET /api/v1/campaigns/{id}/performance first. Resolve controls.pending before creating another command. Then send:
POST /api/v1/campaigns/{id}/controls
Authorization: Bearer $MR_API_KEY
Idempotency-Key: finish-campaign-001
Content-Type: application/json
{
"action": "close",
"expected_revision": 4,
"reason": "The owner-approved test is finished."
}
Use the current control revision, not the example number. Only previously approved campaigns can close. If initial launch remains unresolved, resolve that operation first. Unapproved drafts have no paid reservation to release.
Confirm the outcome
close_requested_at means the permanent stop request was recorded; it does not prove delivery stopped. delivery_closed_at means the provider pause was confirmed (or the sandbox transition completed). Stopping and reporting can lag. Do not infer that the last observed spend is the final cost.
If a response is lost, read performance.controls.pending and retry its exact input and retry_key. Do not invent a new command or another campaign while the original outcome is unknown. A definite failure allows a corrected close request using the current revision and a new key. The permanent close intent remains in force: resume and budget operations return campaign_closing. Further pause requests remain available for protection.
Read charges and returned credit
GET /api/v1/campaigns/{id}/billing requires campaigns:read. It returns the approved pricing, observed media spend and service fees, remaining_reserved_cents and settlement:
- closing: the stop request needs confirmation.
- awaiting_final_statement: delivery is closed; final provider billing still needs verification.
- settled: final costs were verified and unused wallet credit was released exactly once.
- review_required: billing differs or later reporting needs investigation. A workspace hold may apply.
- closed: a closed campaign without a financial reservation, such as sandbox.
For a settled campaign, settlement includes created_at, media_spend_cents, service_fee_cents and released_credit_cents. Unknown observed amounts remain null. After settlement, GET /api/v1/balance shows the returned credit and a release entry. This is reusable advertising credit, not a card refund. Actual media continues to consume the original operator spending allowance.
Example: a $10 media ceiling plus a maximum $0.50 service fee reserves $10.50. If verified final media cost is $4, the 5% service fee is $0.20 and $6.30 returns to available credit. Historical campaigns keep their original pricing. Payment processing and taxes are separate.
Finality and current limits
Finalization requires MarketingRouter's operator to verify a final provider statement. A stable spend graph, a pause, or waiting a fixed number of hours is not proof of final billing. Customer agents cannot attest finality or choose the amount released. There is no promised automatic settlement deadline yet.
The release workflow supports campaigns funded from the reusable balance. Older direct campaign checkouts need a separate original-payment refund review and cannot be converted into wallet credit by this endpoint. Automatic cash refunds are not implemented.
If a later provider report disagrees with a recorded settlement, MarketingRouter holds the workspace for review instead of spending already-released credit or rewriting its financial history. A manual accounting correction is required. Human owners can use the collapsed Finish this campaign section in Media buying; agents use the same controls and billing API.