Skip to main content
For trying the API. Every block below is a notebook cell: open a Jupyter notebook, save a few real documents beside it, and run the cells in order. Nothing here is an integration pattern, just the shortest path to seeing a real record. You need a workflow whose mode is instant. GET /workflows lists yours with the mode of each, along with how many credits each has left. The documents you send are read as they arrive and stored nowhere, so nothing you run here leaves a file behind. Reach for your uat key, whose token begins uat_, so the runs below come out of a uat workflow’s credits rather than out of what you bought for real work. Whichever key you use, the keys below are placeholders for your own, and a uat workflow’s key ends -uat. The verification is real either way. There is no sandbox: your documents really are read, and really are confirmed with the bodies that issued them.
The timeout is 200 seconds on purpose. The checks are document reads and calls to the issuing registries, and they run before this call answers, so a client on the usual 30-second default gives up on a verification that is still running - and it runs to completion, and is charged for, whether or not you are still listening. Step 6 is how you find one you hung up on.
Every error comes back as {"detail": "..."}. A cell that fails raises on the response it already kept in response, so response.json()["detail"] in a new cell says what went wrong.

1. See what the workflow asks for

Read the workflow first. Its mode says whether you run it this way, and each step’s intake says how it is answered: the slots its documents go in, with how many files each takes and in what formats, and the values it takes.
A step with one slot takes its document without being told which; you only name a slot where a step has more than one. A step with no slots and no values, like cross-check, is answered from what the steps before it established, and you send nothing for it.

2. Read your documents into the request

Each document carries its own bytes, base64-encoded, and names the step it answers.
Send several documents for the same slot where it takes several, as a certificate whose pages arrive as separate images does. Up to 12 documents in one request, and 25 MB between them once decoded. The bank step is left out of the documents on purpose: it takes typed details or a document, and the request below sends the details. To answer it with a cancelled cheque instead, add it to documents with "kind": "cheque", since its slot names more than one kind of proof, and drop the bank entry from steps.

3. Send it

One call carries the workflow, your reference for the subject, the values the steps take and every document. It answers with the finished record.
The cell holds while the checks run, and record is the finished thing. Nothing to poll, and no callback for this call. The call used one credit, because the run it made both started and ended inside it:
A 402 instead of a record means the workflow has no credits left, and no run was made. Read the workflow for credits.remaining and credits.expiring; retrying does not clear it. A 409 means the workflow is already running as many verifications as it may at once. Each call holds a place for as long as it takes to answer, so runs.limit is how many of these you may have in flight; keep at most that many calls open at a time. See Credits and limits. If a call never returns an answer at all, the run it started was still made. Find its record by the reference you sent, in step 6 below, rather than sending the submission again: a second call is a second verification and a second credit.

4. Read what it found

Every step reports a state and, where it did not pass cleanly, the reasons why.
A run worth a second look
A step that passed with a gap is the case worth routing to a person: every document is real and confirmed at source, but something is not an exact match. A step that failed carries the reasons it did not stand up. A step in error was never checked, because a source could not be reached or there was no time left to reach it. Cross-check scores are on the step’s outputs, if you want the numbers rather than the sentences:

5. Ask again with something different

Every call runs. Send the same subject with a different set and you get a second verification of them, answered on what that call carried.
A new record, under the same reference. Nothing was reused from the first call and nothing was reset: reference_user_id is your label for the subject here rather than a key, so several records carry it.

6. Find a run you did not see the answer to

A call that reached us ran the checks whether or not the reply got back to you. List by the reference to find it.
Newest first, so the run you just made is at the top. Read any of them in full with GET /verifications/{verification_id}.

7. See what a bad submission does

Leave out a document the workflow requires and the request is refused before anything runs.
Nothing was checked and nothing was charged for, and no record was created. The same 422 covers a slot you filled only part of, which reads gst: slot 'gst-certificate' needs 2 more file(s), a step given both values and a document, and a document naming a step your workflow does not declare. Correct the request and send it again.
A file the step cannot use is refused the same way, naming it by its position: 415 for a format the slot does not take, 413 for one over the size limit, which is 20 MB for a PDF and 10 MB for an image.

8. Purge it

The documents were never kept, but the values you sent and what was read off each file are on the record. Purge deletes them and closes it for audit.

The record you just read

The finished record behind the run above, from the vendor-check workflow. Yours differs: which steps a verification runs comes from the workflow configured for your integration. The envelope around them is the same for everyone.
  • Nobody signs in, so mobile, return_url and journey_url are null, and callback is null too: the reply carried the result, so there was nothing to hand off.
  • run_requested_at is when the request arrived, and completed_at is a minute or so later. The whole run happened between them, inside the call.
  • udyam is not-supplied. Nothing arrived for it and it is optional, so the run closed over it. collected is null, no source was called, and it is not a verdict about the business - only the absence of a check.
  • The cross-check passed with a gap. The bank returned AVESCO HOSPITEX where the business is registered as AVESCO HOSPITEX PRIVATE LIMITED: close enough not to be a different business, not close enough to call an exact match. outputs.reference names which name it scored against and outputs.matches carries the score behind every check, so you can route on the number rather than the sentence.
  • The documents carry no bytes you can reach. Each one names its step, slot, type, size and hash, which is what the record keeps once the files are gone.
Reading the result is the field-by-field reference for a step.
When you move from trying it to building against it, read Instant for what is decided for you and why, and Going live before your first real subject.