Skip to main content
On the DETERMINATION tier MicroCrop determines and you settle. This is the whole integration, in order. Nothing here moves money through MicroCrop.
Check you are on this tier: GET /api/organizations/medata.serviceTier. New organizations start on DETERMINATION. What matters at runtime is the policy’s frozen settlementMode, not your current tier — see Service tiers.

1. Prerequisites

Your organization must be KYB-verified with a current regulator licence, and MicroCrop must have recorded your underwriting capacity. See Onboarding. Set a webhook endpoint so you are told when a determination is issued:

2. Sell the cover

Register the farmer and plot, quote, then purchase — identical to any other integration (Quickstart):
Check settlementMode on the response and store it. It is frozen here and it is the field every downstream gate reads. methodologyVersion is frozen at the same moment — the determination will be computed under that version, not whatever is current later. paymentInstructions.amount is the premium you should collect. MicroCrop will not collect it.

3. Activate the policy with off-platform premium

Collect premium however you already do — your own M-Pesa till, bank transfer, deduction from an input loan, cash at the depot — then attest it:
Returns 200 with the policy at status: ACTIVE, premiumPaid: true. Requires the policy:activate capability, which your organization API key holds. MicroCrop records an audited PREMIUM transaction marked manual: true and providerVerified: false, with your reference and reason and the identity of the caller — deliberately distinguishable from money a payment provider actually settled. Refusals:
Do not call POST /api/payments/initiate on a Tier 1 policy. It returns 403 TIER_NOT_ENTITLED before writing anything or sending any STK push.

4. Receive determination.delivered

When the index is evaluated for a policy, MicroCrop issues a signed determination and fires the webhook exactly once. Verify the HMAC signature, then act on data:
If you do not run a webhook endpoint, poll instead:
triggered: false is a successful determination, not a failure. amountOwed is zero, settlement.status is NO_PAYOUT_DUE, and no reporting duty is created. Nothing further is expected of you.

5. Fetch the determination and its evidence package

The determination response separates the three facts — see Determinations for the full shape. The two fields you settle against:
amountMinor is authoritative and is a decimal string of minor units — 1650000 minor units is KES 16,500.00. It is never a JSON number, because a large KES or GHS amount would lose precision as an IEEE-754 double. amount is a human convenience string.
Before paying, check for settlement.amountOwedDiscrepancy. It appears when the amount derived from your policy disagrees with the amount inside the signed canonical determination. MicroCrop serves the derived figure, but the two must agree — do not settle until the discrepancy is reconciled. Likewise, if amountOwed is null, amountOwedUnavailableReason says why (an unregistered currency exponent); MicroCrop will not guess a scale for a monetary obligation, and neither should you.
The evidence package is the artifact you archive and hand to a regulator or reinsurer. It verifies offline — see Independent verification. Verify it before you pay; that is the point of buying it.

6. Settle the farmer

This step has no MicroCrop API call. Pay the farmer through your own rails, in the policy’s own currency, in line with your policy wording and your regulator’s requirements. [PLACEHOLDER — FOR COUNSEL] Any customer-facing wording about the basis of payment, the finality of a parametric determination, or the farmer’s recourse belongs here and must be drafted by your legal team. MicroCrop does not supply it.

7. Tell MicroCrop what you paid

Post a settlement report so your record sits next to our determination — complete for your regulator and your reinsurer.
201 on a first recording, 200 with replayed: true on a repeat. Requires the settlement:report capability — ORG_FINANCE and ORG_ADMIN only, which is what your organization API key holds.

Making it idempotent

The idempotency key is (determinationId, partnerReference), and partnerReference is a body field rather than a header on purpose: an operator has to be able to ask “have you already told us about M-Pesa code QK73HG9XYZ?”, and a per-attempt header key would let the same payment be attested twice. So partnerReference must be stable per payment. Use the settlement reference your own rail produced — the M-Pesa code, the bank reference, the receipt number. A fresh UUID per retry writes two attestations that the same money moved once. Retrying a timed-out POST with the same reference is safe: it returns 200, replayed: true, and the stored report byte-identical, with no second row, no status transition and no second webhook.

What MicroCrop enforces

settledAmountMinor must be a canonical decimal string: no sign, no decimal point, no exponent, no leading zeros ("0" is the only value that may start with 0). "007" and "7" are rejected as two spellings of one amount inside an append-only record.
MicroCrop never verifies these reports. Every response restates verifiedByMicrocrop: false and verificationStatus: "UNVERIFIED", and the verification field is a one-valued enum so no code path can say otherwise. The report is your statement, stored and echoed back.

Corrections

Reports are append-only; nothing is ever edited. To correct one, POST a new report with a new partnerReference and supersedesReportId set to the id of the live report it replaces:
The old row is stamped supersededAt / supersededByReportId with every attested value intact, and the whole chain stays readable. supersedesReportId must name a live report on this determination — an unknown id, another determination’s report, or an already-superseded row is a 400. Each new report — corrections included — emits determination.settlement_report_recorded with correction: true and report.supersedesReportId, so your own subscribers can replace rather than append.

Reading the chain back

current is the newest un-superseded report; reports is the full chain, newest first, including superseded rows — the chain is the audit trail.

8. The reporting window

When a Tier 1 determination triggers and something is owed, MicroCrop stamps it AWAITING_PARTNER_REPORT with a due date (30 days by default; a MicroCrop operator sets PARTNER_SETTLEMENT_REPORT_DUE_DAYS). The due date is frozen at issue, so changing the setting never re-ages determinations already issued. A late report is still a report — OVERDUE → REPORTED is allowed, and lateness survives on dueAt versus reportedAt rather than being erased.
OVERDUE is a reporting state and nothing more. MicroCrop did not settle the policy, owes nothing on it, and has moved no money.

What you will never see on Tier 1

  • GET /api/payouts returns an empty list. There are no Payout rows — by design, not by omission.
  • payout.completed and payout.failed never fire.
  • No USDC figure and no chain identifier appears anywhere in a Tier 1 determination response, in either the API or the webhook. The response is machine-checked for this before it is served.
  • POST /api/payments/initiate and POST /api/payouts/{id}/retry are permanent 403 TIER_NOT_ENTITLED.

Verify it yourself

Reproduce our hash and recover our signature offline.

Determinations reference

The full response shape and every field.