Features

External API verification

Connect a membership directory to email verification without maintaining an address list.

Big questions. Little guide. Let’s figure it out.

What the API decides

When External API is available in your Pro or Scale server's verification setup, your directory decides whether an email may receive a verification code. Abhi still verifies email ownership, applies account limits, and grants Discord access. An API approval alone never verifies a member.

Connect in five steps

  • Open Members & Roles → Verification → Setup → Who can verify → External API.
  • Choose the Abhi-standard endpoint or map an existing authenticated HTTPS JSON POST API.
  • Enter your endpoint and credential. Review the shared fields and, for an existing API, choose the response field and explicit allow and deny values.
  • Save the draft, then test a known-allowed and a known-denied email. Both tests must pass on the exact saved revision within 24 hours. Tests send no emails and grant no roles.
  • Review the source being replaced and confirm Activate source. This does not activate or change your Discord permission gate.

Build a compatible endpoint

Download a [Node/Express example](/verification-api/express.mjs), [Python example](/verification-api/fastapi_example.py), [PHP example](/verification-api/index.php), or [Cloudflare Worker example](/verification-api/worker.mjs). Use the [OpenAPI contract](/verification-api/openapi.yaml) and [public signature fixtures](/verification-api/signature-fixture.json) to validate your implementation. Never use the fixture secret in production.

Set ELIGIBILITY_HMAC_SECRET in your provider's server-side environment. The examples use ELIGIBILITY_ALLOWED_EMAILS for a small demonstration list; replace that lookup with your real directory. Install your example's framework dependencies in a separate project. Run Node with node express.mjs, Python with uvicorn fastapi_example:app, or PHP with php -S 127.0.0.1:8080 index.php behind an HTTPS reverse proxy. Deploy worker.mjs through your existing Cloudflare Workers project.

Abhi sends schema_version, an opaque request_id, tenant.guild_id as a string, and subject.email. Discord user ID is included only if you opt in. Return HTTP 200 with exactly the decision "allow" or "deny" in a JSON object. The wizard shows the complete request shape.

HMAC SHA-256 signs six lines joined with newline characters, with no final newline: abhi-eligibility-v1, POST, the request target, the timestamp, the request ID, and the hexadecimal SHA-256 body hash. X-Abhi-Signature contains v1= followed by the hexadecimal signature. X-Abhi-Timestamp and X-Abhi-Request-ID carry the signed values. Verify the raw body, compare signatures in constant time, and accept timestamps within five minutes. The examples implement this check.

Connect an existing API

Use the optional cURL importer to parse an inline JSON POST request. It never executes commands. File reads, shell expansion, redirects, insecure TLS options, and proxy headers are rejected. The example email becomes a dynamic request field; review every imported setting before saving.

Paste a sample response and select its decision field. JSON Pointer uses / between fields, ~1 for a literal slash, and ~0 for a literal tilde. Explicit values are compared by exact type: true is not "true" or 1. Missing fields, unexpected values, and invalid JSON are failures, not approvals. Samples and pasted cURL commands are not stored after you leave the wizard.

Understand failures and privacy

Each new code or resend requires a fresh eligibility check. Approval lasts for that code's ten-minute lifetime. Submitting a code does not call your directory again. Requests have an eight-second deadline, at most three attempts within one minute, and 16 KiB request and response limits. Authentication errors, invalid responses, and denials are not retried automatically.

Your provider receives the normalized email, server ID by default, any configured static fields, and the Discord ID only when enabled. Keep credentials in the authentication field, never in URLs or static JSON. Credentials are encrypted and write-only. Abhi retains masked check metadata for up to 48 hours and test receipts for 24 hours, not raw responses. Your provider is responsible for its own logs and retention.

Switch safely or replace credentials

Draft edits and tests leave the active source unchanged. Test replacement credentials before activation; your provider may need to accept old and new credentials temporarily. Source activation invalidates outstanding unused codes but does not revoke verified members. Existing Google Sheets Discord-ID writeback continues for matching rows, with warnings for missing or ambiguous rows. No rows are created automatically.

Troubleshoot

  • Worker unavailable: ask your operator to check the ops worker, schema version, and matching verification encryption keys. You can still save a draft.
  • Authentication failure: check the credential, selected authentication method, and provider clock for HMAC. Test both outcomes again after replacement.
  • Invalid response: check the JSON content type, exact field path and value types, and the 16 KiB response limit.
  • Unavailable: check provider health and public HTTPS reachability. Private networks and redirects are not supported. Verification never falls back to a different source.
  • A test disagrees with its expected result: confirm that the sample email really is allowed or denied by the provider. HTTP 200 does not prove ownership or authentication enforcement.