Skip to Content
API ReferenceToken gates

Token gates API

Public check endpoint + authenticated CRUD. CRUD requires IdentityOrgPermission.TokenGatesEdit / .TokenGatesView. The /check endpoint is public — no auth required.

For the conceptual model — when to use gates, multi-wallet policies, caching — read Token gates.

Create a gate

POST /identity/organizations/:orgSlug/token-gates

Requires org.tokenGates.edit.

Request

{ name: string // 1–80 chars description?: string // up to 500 chars mintAddress: string // base58 Solana SPL mint minHoldings: string // raw token units, decimal string enabled?: boolean // default: true }

Response (201)

interface TokenGate { id: string // 'gate_…' org: string name: string description?: string mintAddress: string minHoldings: string enabled: boolean createdAt: string updatedAt: string }

List gates

GET /identity/organizations/:orgSlug/token-gates

Requires org.tokenGates.view.

Returns { items: TokenGate[], total: number }.

Get one

GET /identity/organizations/:orgSlug/token-gates/:gateId

Same shape as a list item.

Update

PATCH /identity/organizations/:orgSlug/token-gates/:gateId

{ name?: string description?: string | null minHoldings?: string // raw token units enabled?: boolean }

mintAddress is immutable — create a new gate if the token changes.

Delete

DELETE /identity/organizations/:orgSlug/token-gates/:gateId

Returns 204.

Check access (public)

GET /identity/organizations/:orgSlug/token-gates/:gateId/check

No authentication. Public endpoint.

Query params

ParamTypeRequiredNotes
walletAddressstringyesbase58 Solana address
refreshtruenoForce fresh on-chain lookup, bypass cache. Rate-limited per IP.

Response (200)

{ hasAccess: boolean balance: string | null // raw units; null if wallet not found reason: null | 'insufficientBalance' | 'gateDisabled' | 'walletNotFound' }

Behavior

  • Balances are cached for 5 minutes by default. Refresh via ?refresh=true.
  • Cached responses include the cache age via Age HTTP header.
  • The endpoint never reveals mintAddress or minHoldings in the response.

Errors

HTTPcodeWhen
404not_foundGate id does not exist or org slug is wrong.
422validation_failedwalletAddress is not a valid base58 address.
429rate_limitedToo many refresh=true calls from your IP.

Bulk check (planned)

A bulk endpoint for checking N wallets against M gates in one round trip is planned for the next release. Current workaround: call /check per wallet × gate.

Privacy and abuse

  • Gate IDs are part of the URL; they are not secret.
  • Anyone with a gate id + a wallet address can run the check.
  • The mint address and minimum holdings are not in the check response, only the boolean and balance.
  • If your gate criteria are sensitive, don’t share the gate id with untrusted parties; treat it like a moderately secret config value.

Rate limits

EndpointLimit
POST/PATCH/DELETE /token-gates30/min/org
GET /check (no refresh)600/min/IP
GET /check?refresh=true60/min/IP

Example

async function userHasAccess(userId: string, gateId: string) { const wallets = await synq.users.publicKeys.list(userId) // Any-wallet policy for (const w of wallets) { const r = await fetch( `https://synq.id/identity/organizations/${ORG_SLUG}/token-gates/${gateId}/check?walletAddress=${w.address}`, ) const { hasAccess } = await r.json() if (hasAccess) return true } return false }
Last updated on