Skip to main content
MicroCrop POSTs events to an HTTPS endpoint you control, so you don’t have to poll. Each delivery is signed so you can verify it came from MicroCrop, retried with backoff if your endpoint is unavailable, and logged so you can replay one by hand.

1. Configure your endpoint

Webhook configuration lives under /api/organizations/me/webhook. Every route below requires ORG_ADMIN — your organization API key holds it.
A webhookSecret is generated automatically the first time you set a webhookUrl, and PUT returns it in the same response. Read it back any time:
Rotate it if it leaks — the new value is returned once, and deliveries signed with the old secret stop verifying immediately:
PUT /api/organizations/me/settings does not configure webhooks. It accepts only brandColor and contactPhone; a webhookUrl sent there is silently discarded and the call still returns 200. Use PUT /api/organizations/me/webhook.GET /api/organizations/me does not return webhookSecret either — it strips it, along with apiKey and apiSecret. Use GET /api/organizations/me/webhook.

2. Events

The three determination.* events are the whole notification surface for a Tier 1 integration; payout.* never fires there, because MicroCrop settles nothing.

3. Payload & headers

Every delivery is a JSON POST with this envelope:
Headers:

determination.delivered

Its data is currency-neutral and chain-neutral in both service tiers — one payload shape, one parser:
amountOwed is stated in your policy’s own currency, derived as sumInsured × damagePercentBp / 10000 truncated to the minor unit — never USDC, and with no exchange rate anywhere in the path. If your policy’s currency has no registered minor-unit exponent, amountOwed is null and amountOwedUnavailableReason is present instead. See Determinations. On the DETERMINATION_AND_SETTLEMENT tier you still receive the settled figure separately on payout.completed, which is where amountUSDC belongs. The event fires once per determination. It is keyed on the same idempotency key MicroCrop uses to ingest the signed determination, so an oracle resubmitting the same envelope emits nothing further. It is not emitted at all for a determination that could not be attributed to one of your policies.

determination.settlement_report_recorded

Fires for every newly recorded Tier 1 settlement report, corrections included. A replayed report — same determinationId and partnerReference — writes nothing and emits nothing.
When correction is true, report.supersedesReportId names the report this one replaces — replace your stored attestation rather than appending a second one.

determination.settlement_report_overdue

Fires at most once per determination, when a Tier 1 determination reaches its reporting due date with no report on file. It is a reporting state and nothing more: MicroCrop did not settle the policy, owes nothing on it and has moved no money.

4. Verify the signature

Compute HMAC-SHA256(secret, "{timestamp}.{rawBody}") and compare, in constant time, to x-microcrop-signature. Use the raw request body (the exact bytes received), never a re-serialized object — key order and whitespace would differ and the comparison would fail.
Respond 2xx quickly — MicroCrop aborts the request after 10 seconds and treats the timeout as a failed attempt. Do heavy work asynchronously.

5. Retries & delivery semantics

  • Non-2xx responses (or timeouts) are retried up to 5 attempts with exponential backoff (~30s, 1m, 2m, 4m, 8m). After the last attempt the delivery is marked FAILED.
  • Delivery is at-least-once — the same event may arrive more than once. Deduplicate by id (or x-microcrop-delivery); automatic and manual retries re-send the same body.id, so deduplicating on it is safe across every path.
  • Order is not guaranteed; treat each event as a state assertion and reconcile.

Delivery log

status filters on PENDING, DELIVERED or FAILED; limit is capped at 100. The delivery payload is not returned by this endpoint — only the attempt metadata.

Manual retry

Re-enqueues the stored payload against your current webhookUrl and current secret, resets the row to PENDING, and re-sends the same body.id. It returns 400 INVALID_INPUT if the organization has no webhook URL configured, and 404 NOT_FOUND for a delivery id that is not yours.

6. Fallback: polling

Webhooks are a convenience layer over the same state you can always poll. If you miss deliveries beyond the retry window, reconcile with GET /api/determinations, GET /api/policies/{id}/status and (on Tier 2) GET /api/payouts.
Need an event we don’t emit yet (e.g. policy.expired, payout.processing)? Tell your MicroCrop contact — the event set is easy to extend.