Skip to content

Request links

Ask one customer for documents, camera captures, answers and identity checks through a single link, and get everything back in one record.

View as Markdown

A request link is a custom information request for one customer. You compose what you need, Speedmap gives you a link (or sends it by email or SMS), the customer answers everything on one branded, mobile-first page, and you read it all back in one record: files, answers, capture results and verification references.

Typical use: a customer you already onboarded, where you later need more evidence. "Upload your NIN slip." "A photo of a recent utility bill." "A selfie holding your ID." "Your current address." Or a real NIN or BVN check, run through the engine like any other verification.

Item types

Up to 15 items, answered in the order you give them. Each item has an optional label and instructions (plain text, shown to the customer) and a required flag (default true; optional items can be skipped).

TypeWhat the customer doesWhat you get
uploadPicks a file or takes a photo: JPG, PNG or PDF, up to 10 MB each. accept limits the types, multiple allows up to 5 files.The file(s), encrypted at rest, downloadable through the API or the dashboard. Collected only: no OCR or matching.
captureA guided camera step from the capture SDK: document, selfie, selfie_with_id (selfie, then the document, face-matched) or liveness. document_types limits the documents accepted.A capture session with one verification per step (billed as Document and Liveness).
questionAnswers in the format you set: text, date (YYYY-MM-DD), select (one of options) or yes_no.The answer, sealed at rest and returned in clear on the request.
verificationTypes the inputs the product needs: nin (NIN plus a selfie), bvn (BVN, name, date of birth), account (account number and bank) or face (selfie).A normal verification linked by reference_id, billed at the product's price. The outcome is never shown to the customer.

An upload labelled "NIN slip" is just a file. A verification item with product: "nin" runs the real check.

What it costs

  • Every completed request in live mode is charged the request fee (currently ₦50) to your wallet, as a ledger charge noted "Request link". The fee is held when the link is created, charged when the customer finishes, and released if the link expires or you cancel it. If the wallet cannot cover the hold, creation fails with 402 insufficient_balance.
  • Verifications and captures inside the request are charged at their normal price when they run, exactly like a direct API call.
  • Sandbox requests are free, SMS is simulated, and checks answer with the usual test values.

Create a request

POST/v1/requests

Body parameters

  • itemsarrayrequired

    1 to 15 items (see the table above).

  • subjectobject

    name, email, phone, external_id. Sealed at rest; email and phone are returned masked. Required for delivery: email to send by email, phone (Nigerian mobile or E.164) to send by SMS.

  • messagestring

    Shown to the customer above the items. Plain text, up to 1,000 characters.

  • deliverystring[]

    ["email"], ["sms"] or both. Empty (default): you send the url yourself.

  • expires_in_daysinteger

    1 to 30. Default 7.

  • reasonstring

    Purpose code for the checks inside the request. Default financialProducts.

  • brandobject

    name, logo_url, color (6-digit hex) for the customer page. Default: your organization name.

  • metadataobject

    Up to 20 keys, returned on the request and in the webhook.

Request
curl -X POST "https://api.speedmap.io/v1/requests" \
  -H "Authorization: Bearer sk_test_..." \
  -H "Content-Type: application/json" \
  -d '{
  "items": [
    {
      "type": "upload",
      "label": "NIN slip",
      "instructions": "A clear photo or scan of the slip.",
      "accept": [
        "jpg",
        "png",
        "pdf"
      ]
    },
    {
      "type": "upload",
      "label": "Utility bill",
      "instructions": "Issued in the last 3 months.",
      "accept": [
        "pdf",
        "jpg"
      ],
      "required": false
    },
    {
      "type": "capture",
      "kind": "selfie_with_id"
    },
    {
      "type": "question",
      "format": "text",
      "label": "Current residential address"
    },
    {
      "type": "question",
      "format": "select",
      "label": "Employment status",
      "options": [
        "Employed",
        "Self-employed",
        "Student",
        "Retired"
      ]
    },
    {
      "type": "verification",
      "product": "nin"
    }
  ],
  "subject": {
    "name": "Clarke Griffin",
    "email": "clarke@example.com",
    "phone": "08031234567",
    "external_id": "CUST-42"
  },
  "message": "To finish your loan application we need a few more things. It takes about five minutes.",
  "delivery": [
    "email",
    "sms"
  ],
  "expires_in_days": 7
}'
Response 201
{
  "id": "req_7f3a1c9e2b4d6f80",
  "object": "request",
  "status": "pending",
  "mode": "live",
  "url": "https://speedmap.io/r/Qm9wZS1pcy1ub3QtYS1zdHJhdGVneS0yMDI2",
  "subject": {
    "name": "Clarke Griffin",
    "email": "c*****@example.com",
    "phone": "**********4567",
    "external_id": "CUST-42"
  },
  "message": "To finish your loan application we need a few more things. It takes about five minutes.",
  "items": [
    {
      "index": 0,
      "type": "upload",
      "label": "NIN slip",
      "required": true,
      "accept": [
        "jpg",
        "png",
        "pdf"
      ],
      "multiple": false,
      "status": "pending",
      "files": []
    },
    {
      "index": 1,
      "type": "upload",
      "label": "Utility bill",
      "required": false,
      "accept": [
        "pdf",
        "jpg"
      ],
      "multiple": false,
      "status": "pending",
      "files": []
    },
    {
      "index": 2,
      "type": "capture",
      "label": "Selfie and ID document",
      "kind": "selfie_with_id",
      "required": true,
      "status": "pending"
    },
    {
      "index": 3,
      "type": "question",
      "label": "Current residential address",
      "format": "text",
      "required": true,
      "status": "pending"
    },
    {
      "index": 4,
      "type": "question",
      "label": "Employment status",
      "format": "select",
      "options": [
        "Employed",
        "Self-employed",
        "Student",
        "Retired"
      ],
      "required": true,
      "status": "pending"
    },
    {
      "index": 5,
      "type": "verification",
      "label": "NIN check",
      "product": "nin",
      "required": true,
      "status": "pending",
      "reference_id": null
    }
  ],
  "delivery": {
    "email": {
      "requested_at": "2026-10-30T09:12:04.118Z",
      "outbox_id": "eml_2c1f0a9b8d7e6f54",
      "to": "c*****@example.com",
      "status": "queued"
    },
    "sms": {
      "requested_at": "2026-10-30T09:12:04.402Z",
      "status": "sent",
      "provider": "fintava_sms",
      "to": "**********4567"
    }
  },
  "fee_kobo": 5000,
  "fee_status": "held",
  "consent_at": null,
  "expires_at": "2026-11-06T09:12:04.000Z",
  "completed_at": null,
  "created_at": "2026-10-30T09:12:04.000Z"
}

The url is shown once

Speedmap stores a hash of the link, not the link. Copy url from the create response, or let Speedmap deliver it. Resend issues a new link (the old one stops working) and sends it again.

What the customer sees

The link opens speedmap.io/r/{token}: your name, your message, the list of items and a consent notice. The customer accepts the notice, then answers the items in order; progress is saved per item, so they can close the page and come back. Verification items collect the inputs on the page (the number, a selfie) and show only "received" or "try again", never the record. Expired, cancelled and completed links show a clear state.

Read the record

GET/v1/requests/{id}
Items on a completed request
{
  "id": "req_7f3a1c9e2b4d6f80",
  "object": "request",
  "status": "completed",
  "mode": "live",
  "subject": {
    "name": "Clarke Griffin",
    "email": "c*****@example.com",
    "phone": "**********4567",
    "external_id": "CUST-42"
  },
  "message": "To finish your loan application we need a few more things. It takes about five minutes.",
  "items": [
    {
      "index": 0,
      "type": "upload",
      "label": "NIN slip",
      "status": "done",
      "files": [
        {
          "id": "rqf_9a8b7c6d5e4f3a21",
          "filename": "nin-slip.jpg",
          "mime": "image/jpeg",
          "size_bytes": 418221,
          "sha256": "3f2c…",
          "purged": false
        }
      ]
    },
    {
      "index": 1,
      "type": "upload",
      "label": "Utility bill",
      "status": "skipped",
      "files": []
    },
    {
      "index": 2,
      "type": "capture",
      "label": "Selfie and ID document",
      "status": "done",
      "capture_session_id": "cap_1d2e3f4a5b6c7d8e",
      "result": {
        "steps": {
          "selfie": {
            "reference_id": "spm_5e6f7a8b9c0d1e2f3a4b5c6d",
            "status": "verified",
            "match": null
          },
          "document": {
            "reference_id": "spm_6f7a8b9c0d1e2f3a4b5c6d7e",
            "status": "verified",
            "match": true
          }
        }
      }
    },
    {
      "index": 3,
      "type": "question",
      "label": "Current residential address",
      "status": "done",
      "answer": "12 Adetokunbo Ademola Crescent, Wuse II, Abuja"
    },
    {
      "index": 4,
      "type": "question",
      "label": "Employment status",
      "status": "done",
      "answer": "Employed"
    },
    {
      "index": 5,
      "type": "verification",
      "label": "NIN check",
      "status": "done",
      "reference_id": "spm_3f9c1a7b2e4d6f8a0b1c2d3e",
      "result": {
        "status": "verified",
        "match": true
      },
      "verification": {
        "reference_id": "spm_3f9c1a7b2e4d6f8a0b1c2d3e",
        "product": "nin",
        "method": "in_person",
        "status": "verified",
        "match": true
      }
    }
  ],
  "delivery": {
    "email": {
      "requested_at": "2026-10-30T09:12:04.118Z",
      "outbox_id": "eml_2c1f0a9b8d7e6f54",
      "to": "c*****@example.com",
      "status": "queued"
    },
    "sms": {
      "requested_at": "2026-10-30T09:12:04.402Z",
      "status": "sent",
      "provider": "fintava_sms",
      "to": "**********4567"
    }
  },
  "fee_kobo": 5000,
  "fee_status": "charged",
  "consent_at": "2026-10-30T13:58:40.221Z",
  "expires_at": "2026-11-06T09:12:04.000Z",
  "completed_at": "2026-10-30T14:03:11.004Z",
  "created_at": "2026-10-30T09:12:04.000Z"
}

Each item carries its status (pending, done, skipped) and, on the single-request read, its answer, files, verification (the assertion) or the capture result with one reference per step. Download a file with GET /v1/requests/{id}/files/{file_id}; every download is recorded in your audit log as a reveal. Files and sealed answers are purged under your retention policy; the record of what was asked stays.

Resend, cancel, list

POST/v1/requests/{id}/resend
POST/v1/requests/{id}/cancel
GET/v1/requests

resend takes an optional channels array (default: the channels used before) and returns the request with a fresh url. cancel closes the link and releases the fee hold. The list supports status, limit and before like every other list.

Templates

Save a list of items (and a message) you ask for often, then load it when you create a request.

GET/v1/request-templates
POST/v1/request-templates
DELETE/v1/request-templates/{id}
Save a template
curl -X POST "https://api.speedmap.io/v1/request-templates" \
  -H "Authorization: Bearer sk_test_..." \
  -H "Content-Type: application/json" \
  -d '{
  "name": "Address proof",
  "message": "Please send a recent utility bill and confirm your address.",
  "items": [
    {
      "type": "upload",
      "label": "Utility bill",
      "accept": [
        "pdf",
        "jpg"
      ]
    },
    {
      "type": "question",
      "format": "text",
      "label": "Current address"
    }
  ]
}'

Webhook

request.completed fires when the customer answers the last item. It is PII-free: item statuses, verification reference ids, file ids, your external_id and metadata. Read answers and files with GET /v1/requests/{id}. The dashboard user who created the request also gets an in-app notice.

request.completed
{
  "id": "evt_0c9d8e7f6a5b4c3d",
  "type": "request.completed",
  "mode": "live",
  "created_at": "2026-10-30T14:03:11.090Z",
  "data": {
    "id": "req_7f3a1c9e2b4d6f80",
    "object": "request",
    "status": "completed",
    "mode": "live",
    "external_id": "CUST-42",
    "fee_kobo": 5000,
    "items": [
      {
        "index": 0,
        "type": "upload",
        "label": "NIN slip",
        "status": "done",
        "reference_id": null,
        "capture_session_id": null,
        "result": null,
        "file_ids": [
          "rqf_9a8b7c6d5e4f3a21"
        ]
      },
      {
        "index": 1,
        "type": "upload",
        "label": "Utility bill",
        "status": "skipped",
        "reference_id": null,
        "capture_session_id": null,
        "result": null,
        "file_ids": []
      },
      {
        "index": 2,
        "type": "capture",
        "label": "Selfie and ID document",
        "status": "done",
        "reference_id": null,
        "capture_session_id": "cap_1d2e3f4a5b6c7d8e",
        "result": {
          "steps": {
            "selfie": {
              "reference_id": "spm_5e6f7a8b9c0d1e2f3a4b5c6d",
              "status": "verified",
              "match": null
            },
            "document": {
              "reference_id": "spm_6f7a8b9c0d1e2f3a4b5c6d7e",
              "status": "verified",
              "match": true
            }
          }
        }
      },
      {
        "index": 3,
        "type": "question",
        "label": "Current residential address",
        "status": "done",
        "reference_id": null,
        "capture_session_id": null,
        "result": null
      },
      {
        "index": 4,
        "type": "question",
        "label": "Employment status",
        "status": "done",
        "reference_id": null,
        "capture_session_id": null,
        "result": null
      },
      {
        "index": 5,
        "type": "verification",
        "label": "NIN check",
        "status": "done",
        "reference_id": "spm_3f9c1a7b2e4d6f8a0b1c2d3e",
        "capture_session_id": null,
        "result": {
          "status": "verified",
          "match": true
        }
      }
    ],
    "consent_at": "2026-10-30T13:58:40.221Z",
    "completed_at": "2026-10-30T14:03:11.004Z",
    "created_at": "2026-10-30T09:12:04.000Z"
  }
}

Key scopes

A scoped key needs the scope of every check or capture inside the request (nin, bvn, account, face, document, liveness). A request that only collects files and answers works with any key.

Dashboard

Verification links → Requests has the same builder: add, remove and reorder items, write the message, pick expiry and delivery, save or load a template. Each request has a page with every answer, file preview and download (audited), verification result, delivery status with resend, and cancel.

Questions? Email developers@speedmap.io. This page is also available as Markdown.