# Voter's Card

> Verify a voter's card against the INEC record. Sandbox only until the INEC data agreement is live.

Verifies the document against the INEC record and reports whether it has expired. This product is in preview: it works with sandbox keys while the INEC data agreement is finalized.

`POST /v1/verify/voters-card`

- **Availability:** Preview (sandbox only)
- **Indicative price:** ₦150.00 per completed check
- **Consent:** Your attestation (`client_attested`).

> **Preview:** Voter's Card answers with sandbox keys only. Live keys receive `403 product_not_live` until the provider agreement is live.

## Request

**Body parameters**

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `vin` | string | Yes | Format: 19 letters or digits. |
| `reason` | string | Yes | Purpose of the check. One of the 38 NIMC purpose codes. See the [purpose code list](https://speedmap-delta.vercel.app/docs/compliance#purpose-codes). |
| `consent` | object | Yes | Your attestation that you obtained the data subject's consent for this purpose. |
| `consent.obtained` | boolean | Yes | Always `true`. |
| `match` | object | No | Optional values to compare against the record. Each comparison is returned in field_matches; match is true only if all of them agree. |
| `match.first_name` | string | No | Must not be empty. |
| `match.middle_name` | string | No | Must not be empty. |
| `match.last_name` | string | No | Must not be empty. |
| `match.date_of_birth` | string | No | Format: date, `YYYY-MM-DD`. |

- `match` is `false` when the document has expired.

Send an `Idempotency-Key` header so a retried request is never billed twice. See [Idempotency](https://speedmap-delta.vercel.app/docs/idempotency).

_cURL_

```bash
curl -X POST http://localhost:4000/v1/verify/voters-card \
  -H "Authorization: Bearer sk_test_..." \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{
  "vin": "90F5B1A2C3D4E5F6071",
  "reason": "financialProducts",
  "consent": {
    "obtained": true
  }
}'
```

_Node.js_

```javascript
const res = await fetch("http://localhost:4000/v1/verify/voters-card", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.SPEEDMAP_API_KEY}`,
    "Content-Type": "application/json",
    "Idempotency-Key": crypto.randomUUID(),
  },
  body: JSON.stringify({
    "vin": "90F5B1A2C3D4E5F6071",
    "reason": "financialProducts",
    "consent": {
      "obtained": true
    }
  }),
});

const verification = await res.json();
if (!res.ok) throw new Error(verification.error.message);
console.log(verification.status, verification.match);
```

_Python_

```python
import os, uuid, requests

res = requests.post(
    "http://localhost:4000/v1/verify/voters-card",
    headers={
        "Authorization": f"Bearer {os.environ['SPEEDMAP_API_KEY']}",
        "Idempotency-Key": str(uuid.uuid4()),
    },
    json={
        "vin": "90F5B1A2C3D4E5F6071",
        "reason": "financialProducts",
        "consent": {
            "obtained": True
        }
    },
    timeout=30,
)
res.raise_for_status()
verification = res.json()
print(verification["status"], verification["match"])
```

## Response

A completed check returns `200` with a [verification object](https://speedmap-delta.vercel.app/docs/verifications). `status` is `verified` or `not_found`. If the provider has not answered in time you get `202` with `status: "pending"`; [retrieve it later](https://speedmap-delta.vercel.app/docs/verifications#pending-verifications) or wait for the `verification.completed` [webhook](https://speedmap-delta.vercel.app/docs/webhooks).

**200 OK · assertion**

```json
{
  "reference_id": "spm_8171f3a87cbe221341f7185f",
  "object": "verification",
  "product": "voters_card",
  "method": "voters_card",
  "status": "verified",
  "match": true,
  "field_matches": null,
  "field_confidence": null,
  "mode": "sandbox",
  "reason": "financialProducts",
  "subject": "***************6071",
  "summary": {
    "first_name": "A*****",
    "last_name": "O*****",
    "document_type": "voters_card",
    "expired": false,
    "expires_at": null
  },
  "consent": {
    "mechanism": "client_attested",
    "consent_id": null,
    "timestamp": "2026-10-19T09:41:07.512Z"
  },
  "source": "api",
  "error": null,
  "billing": {
    "amount_kobo": 0,
    "currency": "NGN"
  },
  "latency_ms": 330,
  "created_at": "2026-10-19T09:41:07.512Z",
  "updated_at": "2026-10-19T09:41:07.874Z"
}
```

**summary**

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `first_name` | string | No | First name on the record, masked to its first letter: `A*****`. |
| `last_name` | string | No | Last name on the record, masked to its first letter. |
| `document_type` | string | No | Document type. |
| `expired` | boolean | No | Whether the expiry date has passed. |
| `expires_at` | string \| null | No | Expiry date, `YYYY-MM-DD`; `null` for documents without expiry. |

Full-tier accounts calling the REST API also receive `provider`, `provider_reference` and `data`, the normalized record. MCP, webhooks, lists and bulk results never include it. See [Response shaping](https://speedmap-delta.vercel.app/docs/response-shaping).

**Full tier only**

```json
{
  "provider": "sandbox",
  "provider_reference": "SBX6CB753DB2C",
  "data": {
    "document_type": "voters_card",
    "document_number": "90F5B1A2C3D4E5F6071",
    "first_name": "CLARKE",
    "middle_name": "ELIZA",
    "last_name": "GRIFFIN",
    "date_of_birth": "1992-04-18",
    "gender": "female",
    "issued_at": "2022-11-15",
    "expires_at": null,
    "expired": false,
    "issuing_authority": "Independent National Electoral Commission",
    "polling_unit": "PU 012, Eti-Osa",
    "state": "Lagos",
    "lga": "Eti-Osa",
    "photo": null
  }
}
```

## Test values

Send these bodies with a sandbox key. More outcome triggers are on the [Sandbox](https://speedmap-delta.vercel.app/docs/sandbox) page.

**Valid document**

```json
{
  "vin": "90F5B1A2C3D4E5F6071",
  "reason": "financialProducts",
  "consent": {
    "obtained": true
  }
}
```

**Not found**

```json
{
  "vin": "00F0B0A0C0D0E0F0000",
  "reason": "financialProducts",
  "consent": {
    "obtained": true
  }
}
```

## Errors

| Status | Code | Meaning |
| --- | --- | --- |
| 400 | `invalid_request` | The body failed validation. `details` lists each field. |
| 401 | `unauthorized` | Missing or invalid API key. |
| 402 | `insufficient_balance` | Live only: wallet balance is below the check price. |
| 403 | `not_entitled` | Your workspace is not enabled for this product or method. |
| 403 | `product_not_live` | Live keys: this product is sandbox only for now. |
| 409 | `idempotency_key_reused` | The `Idempotency-Key` was used with a different body. |
| 422 | `provider_rejected_request` | The upstream registry rejected the request. |
| 503 | `verification_unavailable` | Temporary failure. Retry, with the same or a new `Idempotency-Key`. |

All errors share one shape. See [Errors](https://speedmap-delta.vercel.app/docs/errors) for the full list.

---

Source: https://speedmap-delta.vercel.app/docs/products/voters_card
