HoneyGate Docs
Pricing Add to Discord

REST API

Read your server's gates, members, leaderboard, and audit log over a simple authenticated REST API.

HoneyGate exposes a read-only REST API so you can pull your server's data into your own apps, dashboards, or websites. It's on every plan, Free included. This is infrastructure, not an upsell.

Authentication

Create an API key in the admin panel under API Keys (/admin/public-api/). The key is shown once at creation, since we only store a hash, so copy it right then.

Send it as a Bearer token:

Authorization: Bearer $HONEY_GATE_API_KEY

Keys start with hg_live_ and run about 72 characters. Each one is scoped, so you pick what it can read when you create it:

ScopeGrants
gates.readList gates and their conditions
members.readList members (with XP and join dates)
points.readRead points totals, the leaderboard, and rank
audit.readRead audit-log entries
holdings.readRead a member's on-chain holdings (NFT counts and token balances) for your server's tracked assets

There's also an events.read scope for the live Event Stream. That one isn't a REST endpoint on this page; see Outbound webhooks for push-style updates.

A key is tied to one server, and you can hold up to 10 active keys per server. Give a key an optional expiry if you want it to age out.

Base URL & format

Every endpoint lives under:

https://honeygate.app/api/v1/public-api/<endpoint>

Responses are JSON. Success looks like this:

{ "ok": true, "data": { ... }, "request_id": "..." }

Errors look like this:

{ "ok": false, "error": { "message": "...", "code": "..." }, "request_id": "..." }

Common status codes: 200 OK, 401 invalid or expired key, 403 missing scope, 404 unknown endpoint, 429 rate-limited.

Rate limit

120 requests per minute per key by default, and it's configurable per key. Go over and you'll get a 429. Wait, then retry.

Endpoints

GET /gates (scope: gates.read)

Lists the server's gates, each with its role, logic mode (AND/OR), and conditions.

GET /members (scope: members.read)

Lists members with discord_id, username, display name, joined_at, left_at, is_bot, and xp. Supports ?limit= (default 100, max 1000) and ?offset= for paging.

GET /member?discord_id=… (scope: members.read)

Looks up one member instead of paging through everyone. Returns their username, display name, avatar, joined_at, xp, and:

Use staff to decide whether to let somebody into your own admin screens.

It answers about your server only. A HoneyGate platform administrator is not reported as staff of your server, and neither is anyone holding an older platform-wide grant. The question this answers is "does this person run things here", which is the one worth acting on.

GET /leaderboard (scope: points.read)

Top members by XP. Supports ?limit= (default 25, max 500).

GET /audit (scope: audit.read)

Audit-log entries: role grants and revokes, gate passes, config changes. Supports ?limit= (default 100, max 500) and ?since=<ISO datetime> (defaults to the last 7 days).

GET /holdings?discord_id=… (scope: holdings.read)

How much of your server's tracked assets a member holds on-chain. It reads the same warm balance/NFT cache HoneyGate uses for its own gate checks, aggregated across every wallet that member has verified.

It answers only about the assets your server tracks (the ones you add under web3 asset tracking). A wallet holding some other token you don't track is never reported.

Numbers are exact and integer-based, on purpose:

Each asset, and the response overall, carries freshness evidence:

It fails closed, never to a false zero. If an asset's on-chain data can't be read right now (for example the server's Solana provider is briefly unreachable), that asset comes back "available": false with a reason and no amount — never 0. A 0 here always means "we checked and they hold none"; it never means "we couldn't find out." Treat complete: false as "ask again shortly," not as "they hold less."

It returns a wallet_count so you know how many verified wallets fed the answer. It does not return the wallet addresses themselves — a holdings total doesn't require handing out someone's wallets.

{
  "ok": true,
  "data": {
    "guild_id": "123456789012345678",
    "discord_id": "DISCORD_ID",
    "wallet_count": 2,
    "checked_at": "2026-08-13 12:00:00",
    "data_as_of": "2026-08-13 11:42:00",
    "complete": true,
    "assets": [
      {
        "asset_id": "fook",
        "label": "$FOOK",
        "type": "token",
        "mint": "Foo...mint",
        "units": "$FOOK",
        "available": true,
        "amount_raw": "1500000000",
        "decimals": 6,
        "checked_at": "2026-08-13 11:42:00",
        "from_cache": true
      },
      {
        "asset_id": "zombabiez",
        "label": "Animated ZomBabieZ",
        "type": "nft_collection",
        "mint": "Col...addr",
        "available": true,
        "count": 3,
        "checked_at": "2026-08-13 11:50:00"
      }
    ]
  }
}
curl "https://honeygate.app/api/v1/public-api/holdings?discord_id=DISCORD_ID" \
  -H "Authorization: Bearer $HONEY_GATE_API_KEY"

POST /points-award (scope: points.write) — writes

Awards Coins to a member. This is the only endpoint that changes anything, so it has its own scope: a key that reads your leaderboard cannot also create currency. Grant points.write only to an app you trust.

Send JSON:

FieldRequiredNotes
discord_idyesThe member's Discord ID. They must be in your server.
amountyesA whole number above zero, up to 1,000,000 per call.
idempotency_keyyesAny string unique to this award — your own play or transaction id works well.
sourcenoA short label that shows in the ledger, e.g. arcade_win. Defaults to external_api.
notenoA human-readable note, up to 500 characters.

The idempotency_key is the important one. If a request times out you have no way of knowing whether it arrived, so you have to retry — and a retry must not pay someone twice. Send the same key again and nothing is credited the second time:

{ "ok": true, "data": { "applied": false, "balance": 1250, "amount": 250, ... } }

applied: false means "this one was already counted." That is a success, not an error — the player has their Coins. Retrying a timed-out request with the exact same key is always safe, and is what you should do.

Example

curl https://honeygate.app/api/v1/public-api/leaderboard?limit=10 \
  -H "Authorization: Bearer $HONEY_GATE_API_KEY"

Awarding Coins:

curl -X POST https://honeygate.app/api/v1/public-api/points-award \
  -H "Authorization: Bearer $HONEY_GATE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"discord_id":"123456789012345678","amount":250,"idempotency_key":"play-abc123","source":"arcade_win"}'

Want live updates instead of polling?

For push-style integration, reach for Outbound webhooks. HoneyGate calls your URL when things happen, so you skip the polling entirely.