Publisher callback and operations API
These /publisher/v2 configuration and reporting routes use a publisher dashboard bearer session. The actual reward event is a server-to-server delivery from RapidoReach to the HTTPS endpoint you configure. See the reward callback guide for crediting rules.
Callback configuration
| Method | Path | Purpose |
|---|---|---|
| PUT | /placements/{placementId}/callback | Set HTTPS endpoint, callback families, optional IP allowlist and failed redirect URL |
| POST | /placements/{placementId}/callback/rotate | Stage a new one-time secret and key ID; returns 201 |
| POST | /placements/{placementId}/callback/promote | Promote the staged key after its valid time |
| POST | /placements/{placementId}/callback/test | Return a signed in-memory sample; does not deliver externally |
enabledFamilies accepts REWARD_PENDING, REWARD_CONFIRMED, and REWARD_REVERSED. The current callback signature version is HMAC_SHA256_V1. The signed value is HMAC-SHA256 over timestamp.nonce.canonicalJsonBody using the secret identified by keyId. Require a fresh timestamp and nonce, compare signatures safely, and deduplicate by event/transaction identity before crediting. A test response reports deliveredExternally: false and includes a sample payload and headers object; it creates no accounting entry.
The live delivery is an HTTPS JSON POST to your configured endpoint. Headers are X-RapidReach-Key-Id, X-RapidReach-Timestamp (ISO date/time), X-RapidReach-Nonce, X-RapidReach-Signature (lowercase hex), and Idempotency-Key. The canonical body is JSON with object keys sorted recursively, retaining array order; do not hash the raw received byte stream or a differently ordered JSON serialization. The callback payload contains:
| Field | Meaning |
|---|---|
schemaVersion, callbackType | Current contract version and REWARD_CONFIRMED or REWARD_REVERSED delivery type. |
transactionId, originalTransactionId | Reward ID and, for reversal, the original reward ID. |
placementId, adSlotId, externalUserId, campaignId | Identity to reconcile with your ledger and the SDK session. |
virtualAmountMinorUnits, virtualCurrency | Publisher-defined user reward and currency. |
usdPayoutMicros, status, sandbox, occurredAt | Payout amount, reward state, environment, event time. |
{
"schemaVersion": "2.0.0",
"callbackType": "REWARD_CONFIRMED",
"transactionId": "REWARD_TRANSACTION_ID",
"originalTransactionId": null,
"placementId": "PLACEMENT_ID",
"adSlotId": "SLOT_ID",
"externalUserId": "user-123",
"campaignId": "CAMPAIGN_ID",
"virtualAmountMinorUnits": 50,
"virtualCurrency": "Coins",
"usdPayoutMicros": 750000,
"status": "CONFIRMED",
"sandbox": false,
"occurredAt": "2026-09-29T08:00:00.000Z"
}
Persist the callback event and apply a confirmed credit or linked reversal in one idempotent transaction, then return any 2xx response. A non-2xx response triggers a retry; deliveries stop after the configured attempts and can enter a dead-letter state. Keep both the Idempotency-Key and transaction IDs so an operator replay cannot double-credit. The sample payload above is illustrative; use the current body and headers from the test endpoint to verify your implementation.
PUT /publisher/v2/placements/PLACEMENT_ID/callback
Authorization: Bearer SESSION_TOKEN
Content-Type: application/json
{
"endpoint": "https://publisher.example/rewards/callback",
"enabledFamilies": ["REWARD_CONFIRMED", "REWARD_REVERSED"],
"ipAllowlist": []
}
The secret returned by /callback/rotate is displayed once. Store the current and next keys during overlap; the staged key becomes valid at validFrom, and promotion is blocked until then. Never use a dashboard session or Static API key as the HMAC secret.
Delivery and report endpoints
| Method | Path | Response |
|---|---|---|
| GET | /callback-deliveries?status= | {items,count} without full payloads |
| GET | /callback-deliveries/{deliveryId} | One delivery with user ID redacted |
| POST | /callback-deliveries/{deliveryId}/replay | Replay result; requires Idempotency-Key and a reason |
| GET | /reports/offers | JSON metrics, settled rewards, freshness watermark, reconciliation |
| GET | /reports/offers?format=csv | CSV attachment with reward and ledger totals |
| GET | /offer-gallery?placementId=&adSlotId=&country=&platform= | Preview inventory for an owned Offerwall or Offer API slot |
A callback replay may queue a DEAD_LETTER, RETRY_WAIT, or already DELIVERED item. Reusing its idempotency key returns duplicate: true. Your receiver must still deduplicate the delivery. A nonzero reconciliation.differenceMicros or exact: false needs investigation against ledger and callback state before final reporting.
The replay body is {"reason":"Receiver recovered after outage"}; a reason is 3–240 characters. GET /callback-deliveries accepts status and returns at most 100 newest records. Use one delivery's lastErrorCode, attempt count, next attempt, and response status to diagnose the receiver. Treat a DELIVERED status as delivery acknowledgment, not proof that your own balance update succeeded.
Offer operations are gated by the offer delivery capability. Review a 404 not_found against environment availability before treating it as a missing placement.