> ## Documentation Index
> Fetch the complete documentation index at: https://docs.microcrop.app/llms.txt
> Use this file to discover all available pages before exploring further.

# Tier 1 integration

> The DETERMINATION tier end to end: sell cover, activate on your own premium, receive the determination, settle the farmer yourself, report it back.

On the `DETERMINATION` tier MicroCrop determines and you settle. This is the whole
integration, in order. Nothing here moves money through MicroCrop.

<Note>
  Check you are on this tier: `GET /api/organizations/me` → `data.serviceTier`. New
  organizations start on `DETERMINATION`. What matters at runtime is the **policy's**
  frozen `settlementMode`, not your current tier — see
  [Service tiers](/guides/service-tiers).
</Note>

```mermaid theme={null}
sequenceDiagram
  participant You
  participant MC as MicroCrop
  participant F as Farmer
  You->>MC: POST /policies/purchase
  MC-->>You: policy PENDING, settlementMode DETERMINATION
  F->>You: premium (your own rail)
  You->>MC: PUT /policies/{id}/activate
  MC-->>You: policy ACTIVE
  Note over MC: index evaluated
  MC-->>You: webhook determination.delivered
  You->>MC: GET /determinations/{id}/evidence
  Note over You: verify signature + hash offline
  You->>F: settle, off-platform
  You->>MC: POST /determinations/{id}/settlement-report
```

## 1. Prerequisites

Your organization must be KYB-verified with a current regulator licence, and MicroCrop
must have recorded your underwriting capacity. See [Onboarding](/guides/onboarding).

Set a webhook endpoint so you are told when a determination is issued:

```bash theme={null}
curl -X PUT https://app.microcrop.app/api/organizations/me/webhook \
  -H "x-api-key: $KEY" -H "Content-Type: application/json" \
  -d '{ "url": "https://your-system.example.com/microcrop/webhooks" }'
# → data.secret is your HMAC signing secret. Store it.
```

## 2. Sell the cover

Register the farmer and plot, quote, then purchase — identical to any other integration
([Quickstart](/quickstart)):

```bash theme={null}
curl -X POST https://app.microcrop.app/api/policies/purchase \
  -H "x-api-key: $KEY" -H "Content-Type: application/json" \
  -d '{
    "farmerId": "f1a2…", "productType": "CROP", "plotId": "p1b2…",
    "sumInsured": 100000, "coverageType": "DROUGHT", "durationDays": 120
  }'
```

```json theme={null}
{ "success": true, "data": {
  "policy": {
    "id": "44444444-4444-4444-4444-444444444444",
    "policyNumber": "MC-KE-2026-000123",
    "status": "PENDING", "premiumPaid": false,
    "sumInsured": "100000.00", "currency": "KES",
    "settlementMode": "DETERMINATION",
    "methodologyVersion": "crop-dualindex-1.0"
  },
  "paymentInstructions": { "amount": 5000, "policyNumber": "MC-KE-2026-000123", "message": "…" }
} }
```

**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:

```bash theme={null}
curl -X PUT https://app.microcrop.app/api/policies/44444444-…/activate \
  -H "x-api-key: $KEY" -H "Content-Type: application/json" \
  -d '{
    "paymentReference": "SFH7TQ2K91",
    "reason": "Premium KES 5,000 received on Acme till 555222, reconciled against statement 2026-09-01"
  }'
```

| Field              | Rules                                                                                                                                                |
| ------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------- |
| `paymentReference` | **Required**, 3–200 chars. Your own evidence money was received: till code, bank reference, receipt id. Stored on the policy and on the transaction. |
| `reason`           | **Required**, 10–500 chars. Why cover is being activated without a provider-verified payment. Recorded for audit.                                    |

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:

| Condition                                                   | Response                                                              |
| ----------------------------------------------------------- | --------------------------------------------------------------------- |
| Policy is not `PENDING`                                     | `400 INVALID_INPUT` — "Policy cannot be activated. Current status: …" |
| A provider premium payment is still `PENDING` on the policy | `400 INVALID_INPUT` — so the farmer cannot be charged twice           |
| Policy id is not yours                                      | `404 NOT_FOUND`                                                       |

<Warning>
  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.
</Warning>

## 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](/guides/webhooks#4-verify-the-signature),
then act on `data`:

```json theme={null}
{
  "id": "8a1c…",
  "event": "determination.delivered",
  "createdAt": "2026-09-09T06:14:22.101Z",
  "data": {
    "determinationId": "b1f0c8e2-3f21-4b1a-9a77-2c9f0a5d1e44",
    "policyId": "44444444-4444-4444-4444-444444444444",
    "policyNumber": "MC-KE-2026-000123",
    "kind": "CROP_DAMAGE",
    "methodologyVersion": "crop-dualindex-1.0",
    "triggered": true,
    "damagePercentBp": 1650,
    "assessedAt": "2026-09-02T00:00:00.000Z",
    "canonicalHash": "5e2151…",
    "settlementMode": "DETERMINATION",
    "settledByMicrocrop": false,
    "amountOwed": {
      "amountMinor": "1650000", "amount": "16500.00", "currency": "KES", "exponent": 2,
      "damagePercentBp": 1650,
      "basis": "policy.sumInsured × damagePercentBp / 10000, truncated to the minor unit"
    },
    "links": { "self": "/api/determinations/b1f0…", "evidence": "/api/determinations/b1f0…/evidence" }
  }
}
```

If you do not run a webhook endpoint, poll instead:

```bash theme={null}
curl "https://app.microcrop.app/api/determinations?policyId=44444444-…" -H "x-api-key: $KEY"
```

<Note>
  `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.
</Note>

## 5. Fetch the determination and its evidence package

```bash theme={null}
DET=b1f0c8e2-3f21-4b1a-9a77-2c9f0a5d1e44

curl "https://app.microcrop.app/api/determinations/$DET" -H "x-api-key: $KEY"
curl "https://app.microcrop.app/api/determinations/$DET/evidence" -H "x-api-key: $KEY"
curl "https://app.microcrop.app/api/determinations/$DET/evidence?download=1" -H "x-api-key: $KEY" -o evidence.json
```

The determination response separates the three facts — see
[Determinations](/guides/determinations) for the full shape. The two fields you settle
against:

```json theme={null}
"settlement": {
  "mode": "DETERMINATION",
  "settledByMicrocrop": false,
  "status": "NOT_SETTLED_BY_MICROCROP",
  "reason": "This policy was sold under the DETERMINATION service tier. …",
  "amountOwed": { "amountMinor": "1650000", "amount": "16500.00", "currency": "KES", "exponent": 2, "…": "…" }
}
```

`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.

<Warning>
  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.
</Warning>

The evidence package is the artifact you archive and hand to a regulator or reinsurer. It
verifies **offline** — see [Independent verification](/guides/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.

```bash theme={null}
curl -X POST "https://app.microcrop.app/api/determinations/$DET/settlement-report" \
  -H "x-api-key: $KEY" -H "Content-Type: application/json" \
  -d '{
    "partnerReference": "QK73HG9XYZ",
    "outcome": "SETTLED_FULL",
    "method": "MOBILE_MONEY",
    "settledAmountMinor": "1650000",
    "settlementCurrency": "KES",
    "settledAt": "2026-09-02T09:14:00Z",
    "attestingOfficerName": "A. Officer",
    "attestingOfficerTitle": "Head of Claims"
  }'
```

`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

| Rule                                                                                                               | On failure                                    |
| ------------------------------------------------------------------------------------------------------------------ | --------------------------------------------- |
| `settlementCurrency` must equal the policy's currency                                                              | `400` — MicroCrop never converts on this path |
| `settledAmountMinor` may not exceed `amountOwed.amountMinor`                                                       | `400`                                         |
| The **sum of all live reports** may not exceed the amount owed — instalments are fine, a second full report is not | `400`, naming what is already attested        |
| `settledAmountMinor` equal to the full amount ⇒ `outcome` must be `SETTLED_FULL`                                   | `400`                                         |
| Less than the full amount ⇒ `SETTLED_PARTIAL` with `shortfallReason`                                               | `400`                                         |
| `DECLINED` ⇒ `declineReason` and `settledAmountMinor: "0"`                                                         | `400`                                         |
| `settledAt` not in the future, and not before the determination                                                    | `400`                                         |
| The policy must be Tier 1                                                                                          | `403 NOT_PARTNER_SETTLED`                     |
| A `amountUSDC` / `settledAmountUsdc` / `payoutAmountUsdc` / `payoutId` field in the body                           | `400` — refused loudly, not silently dropped  |

`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.

<Warning>
  **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.
</Warning>

### 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:

```bash theme={null}
curl -X POST "https://app.microcrop.app/api/determinations/$DET/settlement-report" \
  -H "x-api-key: $KEY" -H "Content-Type: application/json" \
  -d '{
    "partnerReference": "QK73HG9XYZ-CORR1",
    "supersedesReportId": "r1111111-1111-1111-1111-111111111111",
    "outcome": "SETTLED_PARTIAL",
    "method": "MOBILE_MONEY",
    "settledAmountMinor": "1600000",
    "shortfallReason": "KES 500 mobile-money fee deducted at source",
    "settlementCurrency": "KES",
    "settledAt": "2026-09-02T09:14:00Z",
    "attestingOfficerName": "A. Officer",
    "attestingOfficerTitle": "Head of Claims"
  }'
```

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

```bash theme={null}
curl "https://app.microcrop.app/api/determinations/$DET/settlement-report" -H "x-api-key: $KEY"
```

```json theme={null}
{ "success": true, "data": {
  "determinationId": "b1f0…",
  "policyId": "4444…",
  "settlementMode": "DETERMINATION",
  "partnerSettlementStatus": "REPORTED",
  "dueAt": "2026-10-02T06:14:22.101Z",
  "verifiedByMicrocrop": false,
  "current": { "id": "r2…", "partnerReference": "QK73HG9XYZ-CORR1", "…": "…" },
  "reports": [ { "…": "…" } ],
  "note": "This is a PARTNER-ATTESTED statement …"
} }
```

`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.

| `partnerSettlementStatus` | Meaning                                                                            |
| ------------------------- | ---------------------------------------------------------------------------------- |
| `AWAITING_PARTNER_REPORT` | Triggered, something owed, no report yet.                                          |
| `OVERDUE`                 | Past `dueAt` with no report. `determination.settlement_report_overdue` fired once. |
| `REPORTED`                | At least one report on file. Terminal.                                             |
| `null`                    | No reporting duty: Tier 2, or the determination did not trigger.                   |

A late report is still a report — `OVERDUE → REPORTED` is allowed, and lateness survives
on `dueAt` versus `reportedAt` rather than being erased.

<Note>
  `OVERDUE` is a **reporting** state and nothing more. MicroCrop did not settle the policy,
  owes nothing on it, and has moved no money.
</Note>

## 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`.

<CardGroup cols={2}>
  <Card title="Verify it yourself" icon="shield-check" href="/guides/verification">
    Reproduce our hash and recover our signature offline.
  </Card>

  <Card title="Determinations reference" icon="file-lines" href="/guides/determinations">
    The full response shape and every field.
  </Card>
</CardGroup>
