Eligibility: Whitelist & Blacklist
The pool-gate is the standalone eligibility service behind every disbursement: it holds a per-deployment whitelist of wallet public keys (W) allowed to participate in the pool, and a blacklist of W values excluded from it. Before including a recipient in a proof, the disbursement agent asks the pool-gate for signed eligibility material (a whitelist signature and a blacklist non-membership proof) and feeds both to the prover as private inputs. A recipient who isn't whitelisted, or who is blacklisted, causes proof generation to fail closed; there is no on-chain fallback and no partial proof.
Management of the two lists is not symmetric. Whitelisting has a caller-facing path through the platform; blacklisting today is a pool-gate operator action with no platform-exposed write API. The sections below reflect that.
Every endpoint on this page keys off the wallet public key W (the secp256k1 affine x‖y, 128 lowercase hex characters, no 0x prefix), not the wallet's on-chain address. W can't be derived from an address; it's only known once the wallet has signed something the platform can recover it from (a SIWX login). See Core concepts for why this matters.
Every call on this page needs a credential; see Authentication & API Access for the SIWX session, OAuth scopes, and admin-key fallback referenced below.
Whitelist
The normal path: automatic at first login
A recipient does not need to be whitelisted explicitly by an integrator in the common case. When a Funding Party onboards a roster via POST /api/employers/onboard, only wallet addresses are known (W isn't derivable from them), so onboarding registers the recipient rows but defers eligibility: the whitelist write happens automatically the first time that recipient signs in (SIWX), at which point their W is recovered from the login signature and pushed to the pool-gate. The onboarding response makes this explicit:
{
"success": true,
"roster": {
"requested": 42,
"employeesAdded": 42,
"whitelist": { "deferred": true }
}
}
The deferred path above assumes the recipient will eventually sign in. A recipient whose wallet is held by an MPC custodian signs nothing at onboarding, and their W cannot be recovered from an address — so waiting is waiting for an event that will not occur. The derivation that would give them an encryption key P cannot work either, since MPC signatures aren't reproducible, which leaves them permanently unpayable rather than merely late.
For those recipients you attest W and register P yourself, from the custodian's own record: Delegated Custody with DFNS → Employees in DFNS custody.
Attesting W directly, without waiting for a login
disburse-agent attest takes a roster of (walletAddress, walletPub) pairs — plus an optional encryptionPublicKey per row — attaches each W, and pushes it to the pool-gate in the same run. It's the path for custodied recipients above, and equally for any integration that already knows its recipients' W and would rather not wait.
It carries its own credential, minted for the roster.attest capability and deliberately not the disburse key: running payroll and deciding who may be paid are different powers. See Authentication & API Access for minting it, and disburse-agent attest --help for the file format, the --dry-run check, and its exit codes.
Repairing or bulk-draining eligibility
If a recipient's first login raced a pool-gate outage, or you're closing a gap across many recipients at once, drive the repair endpoint instead of waiting for each one to log in again:
POST /api/employers/eligibility/resync
Auth: SIWX session, restricted to the onboarding-allowlisted Funding Party wallet (same gate as /onboard).
| Field | Type | Required | Description |
|---|---|---|---|
afterId | integer | No | Cursor: process only rows with id greater than this. Omit (or 0) to start from the beginning. |
force | boolean | No | Re-drive every row holding a real W, ignoring the confirmation stamp. Only meaningful when driven with afterId, since its filter never shrinks on its own. |
curl -X POST https://<platform-host>/api/employers/eligibility/resync \
-H "Cookie: sessionId=<your-session-cookie>" \
-H "Content-Type: application/json" \
-d '{}'
The call is paged: one request repairs at most one batch and returns a cursor.
{
"success": true,
"pending": 3140,
"result": "ok",
"whitelisted": 2980,
"already": 160,
"stamped": 3140,
"nextAfterId": 3140,
"hasMore": true
}
Keep calling with afterId set to the previous nextAfterId until hasMore is false. result: "disabled" means this deployment has no pool-gate configured at all (nothing to repair against); a non-"ok" result on a false success means the pool-gate rejected or failed the batch and the rows remain unrepaired for the next call.
What happens under the hood
The resync call (and first login) ultimately write through to the pool-gate's own admin API, called server-to-server by the platform. It's not something an integrator calls directly, but it's documented here for completeness and for anyone operating a pool-gate instance:
POST {poolGateUrl}/v1/admin/whitelist # single
POST {poolGateUrl}/v1/admin/whitelist/batch # batch
// single
{ "walletPubKey": "a1b2c3...f0a1b2" }
// batch
{ "walletPubKeys": ["a1b2c3...f0a1b2", "c9d8e7...e3f2a1"] }
Auth is the pool-gate OAuth admin:whitelist:write scope, or an X-API-Key fallback when OAuth isn't configured. 201/200 means added; 409 means already present, both treated as success (idempotent). There is no whitelist remove endpoint.
Reading whitelist status
The disbursement agent reads a recipient's whitelist signature directly from the pool-gate before including them in a proof:
GET {poolGateUrl}/v1/whitelist/<W>
{ "signature": "<192-hex EdDSA signature>" }
404 means W is not (yet) whitelisted. Auth is a pool:read-scope bearer token, either minted by the agent itself via client-credentials or vended short-lived by the platform's /agent-edge/bootstrap so the agent never has to hold the raw OAuth client secret.
scripts/utils/scrape-whitelisted.sh in the ligeroclear repo walks every recipient row with a real W and checks it against the pool-gate, reporting which are whitelisted, missing, or unexpected, and can whitelist any gaps it finds with --whitelist-missing. It's an operational/debugging tool run against a specific deployment (it needs RDS and pool-gate credentials pulled from that deployment's running environment), not part of a customer integration.
Blacklist
There is currently no platform- or agent-exposed write path for blacklisting a wallet. Blacklist entries are a pool-gate operator action, enforced entirely in-circuit: the prover proves non-membership in the blacklist as part of every disbursement proof, but neither the platform nor the disbursement agent offers an integrator-facing way to add, remove, or list blacklisted keys.
Reading blacklist non-membership
This is what the agent itself queries, per recipient, before proving:
GET {poolGateUrl}/v1/blacklist/intervals/<prefix>
where prefix is the first 8 hex characters of H(W) (the Poseidon hash of W's four 128-bit limbs, the same hash the circuit uses). The response is a signed adjacency interval:
{
"adjacencySignatures": [
{ "left": "...", "right": "...", "timestamp": 0, "signature": "..." }
]
}
W is proven absent from the blacklist by showing its hash falls strictly inside a signed gap between two adjacent registered entries. Auth is the same pool:read bearer used for whitelist reads.
scripts/utils/blacklist.sh in the ligeroclear repo exercises POST {poolGateUrl}/v1/admin/blacklist (add) against a locally docker-composed pool-gate, and falls back to raw SQL against the pool-gate's own Postgres for remove, status, and list; there is no HTTP endpoint for any of those three. This script is a local test harness for simulating "this recipient is sanctioned" during development. It is not a production integration path; if your deployment needs a wallet blacklisted, that's a request to whoever operates your pool-gate instance.
Verifying signatures independently
GET {poolGateUrl}/v1/public-key needs no auth: it returns the pool-gate's own signing key ({ "components": { "x": "...", "y": "..." } }) so a caller can verify whitelist and blacklist signatures without trusting the agent's own checks.
For a full breakdown of every credential referenced on this page, see Authentication & API Access.