# NIC IMIS — General Public Open API · Integration Guide (for AI coding agents)

> Give this file to an AI coding assistant (Claude, Copilot, Cursor, etc.) as
> context before asking it to build a client library, a webhook receiver, or
> a policy-issuance flow against the NIC IMIS Partner API. It is the source
> of truth for the integration contract.

---

## 1. What this API is

The **NIC IMIS General Public Open API** is a partner-facing REST surface exposed
by the National Insurance Corporation of Tanzania's IMIS platform. A third-party
integrator submits an insurance proposal through this API; IMIS then runs the
standard underwriting flow (premium calculation → approval workflow → GePG
control number → payment → TIRA submission → policy commencement) and pushes
status updates back to the partner-supplied callback URLs along the way.

The API covers **every approved, non-rider product across all lines of business**
(motor, marine, fire, engineering, life, medical, etc.). If a scope of interest
matters, partners narrow the catalogue via product-class **filter tags**.

## 2. Base URLs

Every endpoint is mounted under `/{environment}/mware/general/v1/`.

| Environment  | Base URL                                                   |
|--------------|------------------------------------------------------------|
| UAT          | `https://imis.nicinsurance.co.tz/uat/mware/general/v1`     |
| Production   | `https://imis.nicinsurance.co.tz/production/mware/general/v1` |

All calls are JSON over HTTPS. The spec is OpenAPI 3.0.

## 3. Authentication

Every request must include both of the following headers.

| Header        | Description                                                              |
|---------------|--------------------------------------------------------------------------|
| `API-KEY`     | The bcrypt-hashed API key issued to the partner (opaque string).         |
| `API-SECRETE` | The matching secret. **Note the spelling** — three-letter `SECRETE`.     |

- Keys are issued by NIC operations against a scoped **integration record**
  (see §9 — Integration scope).
- The plaintext key/secret pair is shown **once** at issue or rotation time —
  store both immediately in a secret manager.
- Anonymous (missing headers) → HTTP 401. Wrong headers → HTTP 401.
- Scoped-out integration → HTTP 403 with a machine-readable error code.

## 4. Endpoints (contract summary)

### 4.1 `GET /filter-tags/`

Returns the catalogue of product-class filter tags currently exposed to
partners. Use the returned `filter_tag` values with `GET /products/?filter_tag=<tag>`
to narrow the product list.

Only product classes that have (a) a `filter_tag` assigned and (b) at least
one currently API-eligible product appear.

**Response 200**

```json
{
  "status": true,
  "count": 3,
  "results": [
    {
      "filter_tag": "motor",
      "product_class": { "id": 4, "code": "PCMI01", "name": "Private Motor" },
      "line_business": { "id": 5, "code": "LB03", "name": "Motor Insurance" },
      "product_count": 6
    }
  ]
}
```

### 4.2 `GET /products/?filter_tag=<slug>`

Returns approved, non-rider products. Pass `?filter_tag=<slug>` (optional) to
narrow to a single product class. Unknown tag → HTTP 404 with code
`GENERAL_API_UNKNOWN_FILTER_TAG`.

**Response 200**

```json
{
  "status": true,
  "filter_tag": "motor",
  "count": 6,
  "results": [
    {
      "id": 111,
      "code": "GIT01",
      "name": "Goods In Transit",
      "description": "This policy provides coverage to in-land transit goods.",
      "tira_code": null,
      "policy_term": 1,
      "policy_term_unit": "years",
      "vat_apply": true,
      "line_business": { "id": 5, "code": "LB04", "name": "General Insurance" },
      "product_class": { "id": 23, "code": "MC01", "name": "General Cargo", "filter_tag": "marine-cargo" },
      "form_url": "https://imis.nicinsurance.co.tz/uat/mware/general/v1/products/111/form/"
    }
  ]
}
```

### 4.3 `GET /products/{product_id}/form/`

Returns the dynamic form schema the partner must populate when submitting a
proposal for this product. Field `api_tag` values are the JSON keys you must
use in the `answers` block of the POST body.

**Response 200 (abbreviated)**

```json
{
  "status": true,
  "product": {
    "id": 111,
    "code": "GIT01",
    "name": "Goods In Transit",
    "description": "…",
    "tira_code": null
  },
  "submit_url": "https://imis.nicinsurance.co.tz/uat/mware/general/v1/proposals/",
  "sections": [
    {
      "name": "Cargo",
      "fields": [
        {
          "api_tag": "cargo_value",
          "label": "Cargo value (TZS)",
          "type": "number",
          "required": true
        },
        {
          "api_tag": "cargo_type",
          "label": "Cargo type",
          "type": "select",
          "required": true,
          "options": ["General", "Perishable", "Hazardous"]
        }
      ]
    }
  ]
}
```

**Rules:**
- Every field with `required: true` must be present in the POST body.
- For `type: "select"`, `type: "radio"`, and `type: "checkbox"`, the submitted
  value must match one of the `options` **exactly** (case-sensitive) — the
  same rule the operator sees in the internal underwriter form.
- `type: "number"` accepts floats. `type: "date"` expects ISO-8601 (`YYYY-MM-DD`).

### 4.4 `POST /proposals/`

Submits a proposal. This is the load-bearing endpoint.

**Request body**

```json
{
  "partner_reference": "PARTNER-REF-001",
  "product_id": 111,
  "answers": {
    "cargo_value": 15000000,
    "cargo_type": "General",
    "voyage_from": "Dar es Salaam",
    "voyage_to": "Mombasa"
  },
  "callbacks": {
    "control_number":     "https://partner.example.com/imis/cn",
    "approval_status":    "https://partner.example.com/imis/approval",
    "approval_decision":  "https://partner.example.com/imis/decision",
    "payment":            "https://partner.example.com/imis/payment",
    "tira_status":        "https://partner.example.com/imis/tira",
    "commencement":       "https://partner.example.com/imis/commencement"
  }
}
```

**Rules:**
- `partner_reference` is any string you choose to correlate the submission
  back to your own system. It's echoed on every callback.
- `product_id` must be a currently-eligible product id (see §4.2).
- `answers` keys must match `api_tag` values from §4.3. Any missing required
  field returns HTTP 400.
- `callbacks` is a JSON object. Any subset is allowed; missing channels are
  simply never called.

**Response 201**

```json
{
  "status": true,
  "code": "GENERAL_API_PROPOSAL_RECEIVED",
  "message": "Proposal received; entering approval flow",
  "submission_id": 4231,
  "partner_reference": "PARTNER-REF-001",
  "submit_status": "PENDING_APPROVAL"
}
```

**Common error codes** (HTTP 400 unless stated):
- `GENERAL_API_MISSING_FIELD` — required field absent from `answers`
- `GENERAL_API_INVALID_OPTION` — value not in the field's `options`
- `GENERAL_API_UNKNOWN_PRODUCT` — `product_id` not found (HTTP 404)
- `GENERAL_API_PRODUCT_NOT_ELIGIBLE` — product exists but is not API-eligible
- `GENERAL_API_UNKNOWN_FILTER_TAG` — unknown filter tag (HTTP 404)
- `GENERAL_API_FORBIDDEN` — scope disables this endpoint or tag (HTTP 403)
- `GENERAL_API_INTERNAL_ERROR` — server-side failure (HTTP 500)

### 4.5 `GET /proposals/{partner_reference}/`

Polls the current state of a submission.

**Response 200**

```json
{
  "status": true,
  "id": 4231,
  "partner_reference": "PARTNER-REF-001",
  "submit_status": "TIRA_SUBMITTED",
  "cn_ack": true,
  "approval_status_ack": true,
  "approval_decision_ack": true,
  "pay_ack": true,
  "tira_ack": false,
  "commencement_ack": false,
  "last_error": null,
  "control_number": "992123456789",
  "premium": 145000.00,
  "policy_number": null
}
```

`submit_status` cycles through:
`RECEIVED` → `PENDING_APPROVAL` → `APPROVED` / `REJECTED` → `BILLED` → `PAID`
→ `TIRA_SUBMITTED` / `TIRA_FAILED` → `COMMENCED` / `FAILED`.

## 5. Callbacks (webhooks)

When a proposal moves through a lifecycle state, IMIS POSTs JSON to the
partner-supplied URL for that state. Delivery is fire-and-forget with a
configurable timeout (`GENERAL_API_CALLBACK_TIMEOUT_SECONDS`, default 5s).
Every attempt is recorded in the IMIS audit log.

Each callback body is wrapped as:

```json
{
  "submission_id": 4231,
  "partner_reference": "PARTNER-REF-001",
  "kind": "CONTROL_NUMBER",
  "... kind-specific fields": "..."
}
```

Partners **must respond `2xx`** to acknowledge receipt. Non-2xx responses
leave the corresponding `*_ack` flag false; the dispatcher will retry the
callback the next time the same state transition fires. There is no
automatic backoff (Celery is disabled in this deployment).

### 5.1 Callback kinds

| `kind`               | Fired when                                                | Extra fields                                                                                     |
|----------------------|-----------------------------------------------------------|--------------------------------------------------------------------------------------------------|
| `CONTROL_NUMBER`     | GePG issues a control number                              | `control_number`, `tax_invoice_document_url`                                                    |
| `APPROVAL_STATUS`    | Every workflow stage transition (queued/reviewing/etc)    | `stage`, `status`, `comment`                                                                     |
| `APPROVAL_DECISION`  | Final approval / rejection                                | `decision` (`APPROVED` or `REJECTED`), `comment`                                                 |
| `PAYMENT`            | Payment cleared through GePG                              | `receipt_document_url`, `paid_amount`, `paid_at`                                                 |
| `TIRA_STATUS`        | TIRA cover-note submission result                         | `status` (`SUBMITTED` / `FAILED`), `cover_note`, `sticker_number`, `errors` (on failure)         |
| `COMMENCEMENT`       | Policy commencement finalised                             | `policy_number`, `policy_start_date`, `policy_end_date`, `policy_document_url`, `covernote_document_url` |

## 6. Recommended client patterns

### 6.1 Bootstrapping

1. Call `GET /filter-tags/` once at startup. Cache the tag list.
2. Call `GET /products/` (optionally with `?filter_tag=`). Cache the product ids.
3. For each product you plan to sell, call `GET /products/{id}/form/` and
   cache the section/field schema. Persist the `api_tag` values in your
   client-side form builder.

### 6.2 Submitting

1. Build an `answers` dict keyed by `api_tag`.
2. POST `/proposals/` with a stable `partner_reference` (e.g. a UUID you also
   store on your side).
3. Persist the returned `submission_id`.

### 6.3 Following the lifecycle

You have two options — **pick one, not both**:

- **Callbacks (recommended)**: expose six HTTPS endpoints, one per callback
  kind, and register them in the `callbacks` field of the POST body.
- **Polling**: call `GET /proposals/{partner_reference}/` every 30–60 seconds
  until `commencement_ack` becomes true or `submit_status` becomes `FAILED`
  or `REJECTED`.

## 7. Error handling

- Retry `5xx` responses with exponential backoff (starting at ~1s, capping
  at ~30s, giving up after ~5 minutes).
- Do **not** retry `4xx` — treat as terminal, log, and surface to the operator.
- Both `4xx` and `5xx` return the same JSON shape:
  ```json
  { "status": false, "code": "GENERAL_API_...", "message": "…", "errors": [...] }
  ```
  Log `code` in preference to `message` — codes are stable, messages are not.

## 8. Data hygiene

- Never store the API secret in source control. Use environment variables
  or a secret manager. On rotate, both the key and the secret change.
- Consider signing every callback receiver behind a shared HMAC secret you
  agreed with NIC ops out-of-band (the API does not sign callback bodies
  natively; you can protect the endpoint with header-based allowlisting or
  a per-integration secret you check on receipt).
- All timestamps in requests and responses are naive local time
  (**Africa/Dar_es_Salaam**, UTC+3). No `USE_TZ`.

## 9. Integration scope

Every partner credential is bound to an **integration scope** managed by
NIC operators. A scope pins the credential to one or both surfaces (Marine,
General) and optionally to a subset of product-class filter tags via an
allowlist/blocklist policy.

Effects the client will see:

- **Surface disabled**: `HTTP 403` on every endpoint.
- **Tag allowlist**: `GET /products/` returns only products in the allowed
  classes. `?filter_tag=X` where X is outside the allowlist returns `HTTP 403`.
- **Tag blocklist**: same, but reversed — products in the excluded classes
  are filtered out.
- **Revoked**: `HTTP 401` on every endpoint.

Operators can also **rotate** credentials at any time; the old pair is
deactivated within the same request. Partners must adopt the new pair
provided by NIC ops.

## 10. Getting credentials

Contact **NIC ICT Team** (<ictsupport@nicinsurance.co.tz>) with:

1. The company name and technical contact.
2. The intended use — describe the product/flow you are integrating.
3. The API surface required (`General Public Open API` — this doc).
4. Whether you need scope narrowing (filter tags to allow / exclude).
5. The **callback URLs** you will expose (one per callback kind).

Ops will provision a credential pair, hand you the plaintext exactly once,
and lock the integration to the agreed scope. If lost, the pair is rotated —
never recovered.

## 11. Discovery artefacts

The following are served publicly (no credentials needed) and are safe to
include verbatim in an AI agent's context window:

| Artefact                | URL                                                                                     |
|-------------------------|-----------------------------------------------------------------------------------------|
| Interactive Swagger UI  | `https://imis.nicinsurance.co.tz/uat/mware/general/v1/docs/`                            |
| Raw OpenAPI 3.0 (YAML)  | `https://imis.nicinsurance.co.tz/uat/mware/general/v1/openapi.yaml`                     |
| PDF reference           | `https://imis.nicinsurance.co.tz/uat/mware/general/v1/docs.pdf`                         |
| Postman collection      | `https://imis.nicinsurance.co.tz/uat/mware/general/v1/postman_collection.json`          |
| Postman environment     | `https://imis.nicinsurance.co.tz/uat/mware/general/v1/postman_environment.json`         |

## 12. Prompt-ready snippet for an AI coding agent

Copy the block below into an AI assistant along with this guide:

> You are integrating a client to the **NIC IMIS General Public Open API**.
> The API contract is described in the attached Markdown. Build a strongly
> typed client (language: <your choice>) that:
>
> 1. Reads `API_KEY` and `API_SECRET` from environment variables.
> 2. Exposes `list_filter_tags()`, `list_products(filter_tag=None)`,
>    `get_product_form(product_id)`, `submit_proposal(payload)`, and
>    `poll_status(partner_reference)`.
> 3. Retries `5xx` responses with exponential backoff, never retries `4xx`.
> 4. Exposes an HTTP receiver server (e.g. FastAPI / Express / ASP.NET) with
>    six routes, one per callback kind. Each route logs the payload, marks
>    the corresponding state in a local DB, and returns `HTTP 200 {"ok":true}`.
> 5. Never logs the `API_SECRET` value.
>
> Base URL: `https://imis.nicinsurance.co.tz/uat/mware/general/v1`.
> Headers: `API-KEY: ${API_KEY}` and `API-SECRETE: ${API_SECRET}` on
> every request (note the "SECRETE" spelling — three letters). All
> requests and responses are JSON.
