Consent API

Capture consent inside your own product and get back a record the person, a counterparty or a regulator can verify without asking you. You do not have to buy background checks to use it.

Overview

Under the Digital Personal Data Protection Act, 2023, consent has to be specific, informed and demonstrable. A checkbox in your own database is none of those things once someone disputes it, because the only evidence is a row you control and could have written at any time.

This API produces consent that is evidenced independently. Your server creates a request; the person opens a link, reads the purpose you wrote and agrees or declines; you receive a signed webhook and a reference that anyone can check at verifybg.in/verify/<reference>.

Authentication

Create a key under your account and send it as a bearer token. The key is shown once and stored only as a hash, so it cannot be recovered; if it is lost, revoke it and make another. Your account's email must be confirmed before keys can create requests.

Authorization: Bearer vbg_live_xxxxxxxx_...

Test mode

Test keys never email anyone, never bill, never run checks, and create records that say plainly on every surface that they are tests, including the public verification page. Mode lives in the key itself, so you cannot send test traffic to production by forgetting a flag. A test key sees only test records; a live key sees only live ones.

vbg_test_xxxxxxxx_...   test
vbg_live_xxxxxxxx_...   live

Close the loop in CI without a human signing:

POST /api/v1/consent-requests/{id}/simulate-consent

{ "signed_name": "Test Subject", "method": "EMAIL_OTP" }

This fires your webhooks exactly as a real signature would. It works only with a test key against a test record: a live key gets 403, and a test key pointed at a live record gets 404. No parameter relaxes either.

Create a consent request

POST https://www.verifybg.in/api/v1/consent-requests

{
  "subject_name": "Amit Sharma",
  "subject_email": "amit@example.com",
  "purpose": "Tenancy application for 3BHK, Vasant Kunj",
  "reference": "your-internal-id-4821"
}

purpose is required, up to 200 characters, and is shown to the person verbatim. It appears on the certificate and the public verification page, so write what you would be content for them to read. tier is optional (INSTANT, COMPREHENSIVE or BUSINESS_BULK) and decides which checks the person is told about; a level that is not on sale returns 422.

201 Created

{
  "id": "cms8...",
  "status": "awaiting_consent",
  "consent_url": "https://www.verifybg.in/consent/oVhq...",
  "reference": "your-internal-id-4821",
  "email_sent": true,
  "retention_days": 30
}

With subject_email we email the person the link. Pass "send_email": false to deliver consent_url yourself; that also stops the two reminder emails we would otherwise send.

Check status

GET https://www.verifybg.in/api/v1/consent-requests/{id}

{
  "id": "cms8...",
  "status": "consented",
  "consent_given": true,
  "consent": {
    "declined": false,
    "declined_at": null,
    "decline_reason": null,
    "signed_at": "2026-07-31T15:41:56.642Z",
    "method": "EMAIL_OTP",
    "verify_code": "VBG-7UVACFJY",
    "verify_url": "https://www.verifybg.in/verify/VBG-7UVACFJY",
    "withdrawn_at": null
  }
}

status is one of awaiting_consent, consented, declined or withdrawn.

Branch on consent_given, never on the presence of a verify_code. A refusal carries a reference too, so the person can prove they said no, but consent_given is false and signed_at is null.

Consent methods

method records how the person proved who they were. The three are never presented as equivalent, here or on the certificate.

  • DIGILOCKER: signed in with the government identity service and authorised the release of their details. The strongest.
  • EMAIL_OTP: confirmed a one-time code sent to the email you supplied.
  • TYPED_NAME: typed their name and confirmed, with no independent proof of identity. Used when there is no email.

Webhooks

Add an HTTPS endpoint under your account. Events: consent.signed, consent.declined and consent.withdrawn. Failed deliveries retry at 1, 5, 25 and 125 minutes. Redirects are not followed, and an endpoint that resolves to a private address is refused and switched off.

POST your-endpoint
X-VerifyBG-Signature: <hex hmac-sha256 of the raw body>
X-VerifyBG-Event: consent.signed
X-VerifyBG-Delivery: <delivery id>

{
  "event": "consent.signed",
  "createdAt": "2026-07-31T15:41:57.010Z",
  "data": {
    "request_id": "cms8...",
    "subject_name": "Amit Sharma",
    "verify_code": "VBG-7UVACFJY",
    "method": "EMAIL_OTP",
    "signed_at": "2026-07-31T15:41:56.642Z"
  }
}

Verify the signature on every delivery. Compute HMAC-SHA256 over the raw body, before any JSON parsing, with the signing secret shown when you added the endpoint, and compare it to the header in constant time.

Public verification

Unauthenticated, and the point of the whole thing. Anyone holding a reference can confirm it without a relationship with you or with us.

GET https://www.verifybg.in/api/verify/VBG-7UVACFJY

200  the reference was issued (read "valid" and "status")
404  no such reference was ever issued

A reference that was never issued returns a real 404, so a fabricated certificate cannot pass an automated check. A refusal returns 200 with "valid": false and "status": "declined". Verification reveals the consent and its scope, never the findings of any check.

Errors and limits

Errors return { "error": { "message": "..." } } with a conventional status: 401 for a bad key, 403 when your account email is unconfirmed, 404 for a record that is not yours, 422 for a validation failure with a detail array, and 429 when rate limited. Creation is limited to 300 calls per key per hour.

What this API will not do

  • Return verification findings. Consent and results are separate products, and an integrator authorised for one is not thereby authorised for the other.
  • Bypass the person. No check runs, through the API or otherwise, until they have personally agreed.
  • Keep data indefinitely. Personal data is deleted 30 days after a request is raised. The consent record survives, so a reference keeps verifying after the data behind it is gone.

Need a higher limit, or something not covered here? Write to us.