Bot / Integration Tokens
Long-lived API tokens that let bots and integrations use limited chat and perps APIs on your behalf.
Alfa Club supports per-user bot tokens - long-lived API credentials that act on your behalf, scoped narrowly to chat actions and limited Hyperliquid perps trading. Use them when you want to wire up a bot, a webhook, an analytics job, or external trading automation without keeping a live user session open.
This is the recommended replacement for keeping a Privy session warm and reusing the resulting JWT - that approach is fragile (sessions drop, tokens rotate, scope is unbounded). Bot tokens are explicit, revocable, and rate-limited.
Before you start
A few things to understand:
- The bot acts as you. Messages it sends appear under your username and avatar (with a small 🤖 badge so other keyholders can tell). Perps orders are sent from your room wallet and produce the same in-chat trade cards as app trades, marked with the same bot badge. Treat the token like a password.
- The bot inherits your room access. If you hold a key in room
42, the bot can use its granted chat scopes in room42. Sell the key - the bot loses access on the next request, automatically. Buy a new key elsewhere - the bot can use its granted scopes there too. - Perps trading is creator-only.
perps:tradeonly works for rooms you own, and the token must have a non-emptyroomAllowlist. The bot can manage existing Hyperliquid funds only; bridging remains a manual user action. - Grant only the required scopes.
messages:sendposts chat messages,messages:readreads paginated room history, andperps:tradecan read perps state and submit/cancel Hyperliquid perps orders within the token's trading policy. - Tokens are private. They are shown exactly once, when created. Alfa Club only stores a SHA-256 hash; nobody (including support) can recover a token after creation. If you lose it, revoke and create a new one.
Creating a token
- Open the app and sign in
- Settings → Developer → API Tokens
- Click Create token, give it a descriptive name (e.g.
analytics-bot,discord-relay), and select the scopes it needs - For
perps:trade, set the room allowlist and, if needed, override the default trading caps. Leave Allow leverage changes off unless the bot should be able to raise leverage up to Hyperliquid's asset-specific max. - Copy the plaintext shown in the modal - it starts with
alfa_bot_followed by 32 random characters. You will not see it again. - The list view shows the token's prefix, scopes, trading caps, last-used time, and expiry. Use the Revoke button to invalidate it at any time.
You can hold up to 5 active tokens at a time. Revoke unused ones.
Prefer the least-privileged scope set:
| Scope | Allows | Typical use |
|---|---|---|
messages:send | Post chat messages in rooms you can access. | Discord relays, alerts, webhook bots. |
messages:read | Read chat history in rooms you can access. | Analytics, moderation tools, exports. |
perps:trade | Read perps state and submit/cancel perps orders in allowlisted rooms you own. | External trading automation. |
API reference
The interactive panel below documents the endpoints exposed to bot tokens.
Use the Test it tab to issue a request directly from the browser - paste
your alfa_bot_… token into the Authorization field and a room you hold a
key in.
Authorization
BotTokenAuth Long-lived bot/integration token. Created in
Settings → Developer → API Tokens and prefixed with alfa_bot_.
Shown exactly once at creation; revoke and recreate if lost.
In: header
Path Parameters
Numeric room id.
Header Parameters
Opaque key (≤ 128 chars) for safe retries. Longer values are silently treated as if no key was sent.
length <= 128Request Body
application/json
TypeScript Definitions
Use the request body type in TypeScript.
Response Body
application/json
application/json
application/json
application/json
application/json
application/json
curl -X POST "https://api.alfaclub.app/api/room/42/message" \ -H "Content-Type: application/json" \ -d '{ "body": "hello from my bot" }'{
"ok": true,
"messageId": "6a7dccc8-0000-4000-8000-000000000000",
"deduped": false
}{
"error": "invalid_body"
}{
"error": "invalid token"
}{
"error": "forbidden"
}{
"error": "rate limited (per-token)"
}{
"error": "bot tokens disabled"
}Authorization
BotTokenAuth Long-lived bot/integration token. Created in
Settings → Developer → API Tokens and prefixed with alfa_bot_.
Shown exactly once at creation; revoke and recreate if lost.
In: header
Path Parameters
Numeric room id.
Query Parameters
Page size. Must be greater than 0 and less than 50.
1 <= value <= 49Cursor returned by nextCursor or prevCursor.
Cursor direction. Defaults to false, which pages older messages
using nextCursor.
falseResponse Body
application/json
application/json
application/json
application/json
application/json
application/json
curl -X GET "https://api.alfaclub.app/api/room/42/messages?limit=25"{
"messages": [
{
"date": 1780001062591,
"id": "cebeecea-18c9-4e01-801b-6a115541fe3a",
"sender": "0x0000000000000000000000000000000000000001",
"attachments": [],
"text": "hello from the room",
"reply_id": null,
"reply_date": null,
"reply_text": null,
"reply_sender": null,
"reply_attachments": null,
"reactions": {},
"edit_deadline": 1780001362591,
"is_edited": false,
"isBot": false,
"deleted_at": null,
"deleted_by": null,
"username": "alice"
}
],
"nextCursor": "eyJkYXRlIjoxNzgwMDAxMDYyNTkxLCJpZCI6ImNlYmVlIn0=",
"prevCursor": "eyJkYXRlIjoxNzgwMDAyMDAwMDAwLCJpZCI6Im5ld2VyIn0="
}{
"error": "invalid query"
}{
"error": "missing bearer token"
}{
"error": "missing scope: messages:read"
}{
"error": "rate limited (per-token)"
}{
"error": "bot tokens disabled"
}Authorization
BotTokenAuth Long-lived bot/integration token. Created in
Settings → Developer → API Tokens and prefixed with alfa_bot_.
Shown exactly once at creation; revoke and recreate if lost.
In: header
Path Parameters
Numeric room id owned by the token owner.
Response Body
application/json
application/json
application/json
application/json
curl -X GET "https://api.alfaclub.app/api/room/42/perps/state"{
"data": {
"address": "string",
"clearinghouseState": {},
"openOrders": [
{}
],
"hip3": {}
}
}{
"error": "string"
}{
"error": "string"
}{
"error": "string"
}Authorization
BotTokenAuth Long-lived bot/integration token. Created in
Settings → Developer → API Tokens and prefixed with alfa_bot_.
Shown exactly once at creation; revoke and recreate if lost.
In: header
Path Parameters
Numeric room id owned by the token owner.
Request Body
application/json
TypeScript Definitions
Use the request body type in TypeScript.
Response Body
application/json
application/json
application/json
application/json
application/json
application/json
application/json
application/json
curl -X POST "https://api.alfaclub.app/api/room/42/perps/orders" \ -H "Content-Type: application/json" \ -d '{ "orderIntentId": "018f0947-c4bd-7af0-9f8a-85c6cf57f445", "direction": "open", "orders": [ { "asset": "BTC", "isBuy": true, "price": null, "size": null, "percentage": "100", "reduceOnly": false, "leverage": 35, "currencyIntention": "usd", "sizeCurrencyIntention": "15", "type": { "limit": { "tif": "FrontendMarket" } } } ] }'{
"data": {}
}{
"error": "string"
}{
"error": "string"
}{
"error": "string"
}{
"error": "string"
}{
"error": "string"
}{
"error": "string"
}{
"error": "string"
}Authorization
BotTokenAuth Long-lived bot/integration token. Created in
Settings → Developer → API Tokens and prefixed with alfa_bot_.
Shown exactly once at creation; revoke and recreate if lost.
In: header
Path Parameters
Numeric room id owned by the token owner.
Request Body
application/json
TypeScript Definitions
Use the request body type in TypeScript.
Response Body
application/json
application/json
application/json
application/json
application/json
application/json
application/json
application/json
curl -X POST "https://api.alfaclub.app/api/room/42/perps/tpsl" \ -H "Content-Type: application/json" \ -d '{ "orderIntentId": "018f0947-c4bd-7af0-9f8a-85c6cf57f446", "tpOid": 123456, "orders": [ { "asset": "BTC", "isBuy": false, "price": "69000", "size": "0.01", "reduceOnly": true, "type": { "trigger": { "isMarket": true, "triggerPx": "69000", "tpsl": "tp" } } } ] }'{
"data": {}
}{
"error": "string"
}{
"error": "string"
}{
"error": "string"
}{
"error": "string"
}{
"error": "string"
}{
"error": "string"
}{
"error": "string"
}Authorization
BotTokenAuth Long-lived bot/integration token. Created in
Settings → Developer → API Tokens and prefixed with alfa_bot_.
Shown exactly once at creation; revoke and recreate if lost.
In: header
Path Parameters
Numeric room id owned by the token owner.
Request Body
application/json
TypeScript Definitions
Use the request body type in TypeScript.
Response Body
application/json
application/json
application/json
application/json
application/json
application/json
application/json
application/json
curl -X POST "https://api.alfaclub.app/api/room/42/perps/cancel-orders" \ -H "Content-Type: application/json" \ -d '{ "orderIntentId": "018f0947-c4bd-7af0-9f8a-85c6cf57f447", "orderIds": [ { "asset": "BTC", "oid": 123456 } ] }'{
"data": {}
}{
"error": "string"
}{
"error": "string"
}{
"error": "string"
}{
"error": "string"
}{
"error": "string"
}{
"error": "string"
}{
"error": "string"
}Reading room history
Use GET /api/room/{roomId}/messages with a token that has messages:read.
The response includes message objects plus nextCursor and prevCursor. Some
additional display metadata fields may be present; treat fields not documented
in the API schema as unstable.
Room access is checked on every request. The token owner must hold a key in the
room, unless the room is the default public room. If roomAllowlist is set on
the token, the requested room must also be in that allowlist.
History visibility follows the same rule as the app:
- Room owners can read the full visible room history.
- Non-owner keyholders can read messages sent after their key was created, plus up to 10 older visible messages before that join timestamp.
- The default public room is not subject to the 10-message historical cap.
Trading perps
Use perps:trade for Hyperliquid perps only. The token owner must own the room, the room must be explicitly listed in roomAllowlist, the owner must have accepted the current Hyperliquid terms, and the room must already have completed a manual bridge to Hyperliquid.
The bot cannot bridge, withdraw, distribute, stake, trade spot, or trade Polymarket. It only manages existing Hyperliquid perps funds for allowlisted rooms.
Successful order and close requests automatically emit the normal room trade-completed message. Integrations should not send a separate chat message or trade-card payload; call the perps endpoint with the order body only. Alfa Club builds the trade card server-side and marks the resulting chat message as bot-initiated.
HIP-3 xyz markets
Some Hyperliquid HIP-3 markets live under the xyz DEX namespace. For these markets, use the exact asset id from the xyz universe with the xyz: prefix. For example, the XYZ-100 market is submitted as xyz:XYZ100.
Human-facing shorthand such as xyz-100, XYZ-100, or bare 100 is not accepted by the bot perps endpoint and will fail asset lookup. Do not mix core Hyperliquid assets such as BTC and ETH with xyz: HIP-3 assets in the same order request.
Core Hyperliquid state is returned under data.clearinghouseState and data.openOrders. When HIP-3 state is enabled, xyz positions and open orders are returned under data.hip3.xyz.clearinghouseState and data.hip3.xyz.openOrders.
Example xyz market open:
{
"orderIntentId": "018f0947-c4bd-7af0-9f8a-85c6cf57f445",
"direction": "open",
"orders": [
{
"asset": "xyz:XYZ100",
"isBuy": true,
"price": null,
"size": null,
"percentage": "100",
"reduceOnly": false,
"leverage": 30,
"currencyIntention": "usd",
"sizeCurrencyIntention": "15",
"type": { "limit": { "tif": "FrontendMarket" } }
}
]
}Example xyz market close:
{
"orderIntentId": "018f0947-c4bd-7af0-9f8a-85c6cf57f446",
"direction": "close",
"orders": [
{
"asset": "xyz:XYZ100",
"isBuy": false,
"price": null,
"size": "0.0004",
"percentage": "100",
"reduceOnly": true,
"currencyIntention": "crypto",
"type": { "limit": { "tif": "FrontendMarket" } }
}
]
}HIP-3 opens can still fail if the market is unavailable, requested leverage is above Hyperliquid's asset-specific max, or the room does not have sufficient margin.
Trading policy is stored on the token:
| Field | Default | Server max | Notes |
|---|---|---|---|
maxOrderNotionalUsd | 2500 | 25000 | Enforced after price/size resolution for risk-increasing orders. |
maxAggregateExposureUsdPerRoom | 10000 | 100000 | Counts open positions plus resting risk-increasing orders while the room trade lock is held. |
maxMutationsPerMinute | 6 | 12 | Per-token mutation limiter for order, TP/SL, and cancel endpoints. |
allowLeverageIncrease | false | n/a | When false, bot requests never call Hyperliquid updateLeverage; existing room settings apply. |
Reduce-only closes, reduce-only TP/SL orders, and explicit cancels are treated as de-risking actions and are exempt from notional/exposure caps. Trigger orders must be reduceOnly: true.
Idempotency
For message sends, set an Idempotency-Key header to safely retry network
failures without sending duplicates. Repeats of the same (token, key)
combination within a 5-minute window return the same messageId and set
deduped: true in the response. Different keys are treated as different
messages. Keys must be ≤ 128 characters; longer values are silently treated as
if no key was sent. History reads are naturally idempotent and do not use this
header.
For perps mutations, include a UUID orderIntentId in the JSON body. Reusing
the same orderIntentId returns the stored result while the idempotency record
is retained; using a new UUID creates a new mutation attempt.
The optional percentage field is used only for display and partial-close context. If it is omitted on an open order, the generated trade card defaults it to "100".
Rate limits
Bot token sends and reads use separate token-bucket limits.
| Endpoint | Bucket | Capacity | Refill | Effect when exceeded |
|---|---|---|---|---|
| Send message | Per-token | 5 | 1 message every 3 s | 429 with Retry-After header |
| Send message | Per-owner | 600 | 600 messages per hour | 429 with Retry-After header |
| Read history | Per-token | 60 | 1 request every second | 429 with Retry-After header |
| Read history | Per-owner | 600 | 600 requests per hour | 429 with Retry-After header |
| Perps mutation | Per-token | Token policy | Policy value per min | 429 with Retry-After header |
| Perps mutation | Per-owner | 30 | 30 requests per minute | 429 with Retry-After header |
| Perps mutation | Global | 120 | 120 requests per min | 429 with Retry-After header |
The per-token bucket is the burst limiter. The per-owner bucket is a backstop against creating many tokens to evade per-token caps.
If you need higher throughput, get in touch - we'd rather raise your cap than have you fight the limiter.
Lifecycle
| Event | Effect |
|---|---|
| Create | Token is generated, hashed, stored. Plaintext shown once. |
| Use | last_used_at updated (≤ once per minute per token). |
| Hard expiry | All tokens expire 1 year after creation. |
| Idle revoke | A daily sweep revokes tokens unused for > 90 days. |
| Manual revoke | Press Revoke in Settings → Developer. Effect is immediate - the next request from a revoked token returns 401. |
| Sell your last key | The next request returns 403; no token action needed. Buy back the key and the bot resumes. |
| Account deletion | All your tokens are cascaded out. |
Security and accountability
A few things worth being explicit about:
- You are accountable for what your bot says. Every bot message is internally attributable to the token that sent it. Other keyholders see your username with a 🤖 badge - they know it's a bot but they associate it with you.
- You are accountable for bot trades.
perps:tradecan place real Hyperliquid orders using your room wallet. Use a dedicated token, a narrow room allowlist, conservative trading caps, and revoke the token as soon as you no longer need it. - Read tokens can expose room history. Treat a
messages:readtoken like any other credential with access to private room content. If a tool only needs to post, do not grantmessages:read. - Trading tokens do not custody funds outside the room wallet. The API does not expose bot bridge or withdraw endpoints, but a leaked
perps:tradetoken can still trade existing Hyperliquid funds in allowlisted rooms. - Spam, harassment, or wash-amplification via bots is grounds for action under the same policies as human posts. Repeated abuse from a single owner can result in losing API token privileges entirely (separate from any room-level moderation).
- If a token leaks, revoke it immediately in Settings → Developer. If you suspect compromise we can identify which messages came from the leaked token vs. legitimate use of your account.
- Never commit a token to a public repo. GitHub's secret scanner can be configured to alert on the
alfa_bot_prefix; we recommend doing so.
What's not supported
Bot tokens support chat and the limited Hyperliquid perps endpoints documented above. They cannot search messages, bridge, withdraw, distribute, stake, trade spot, trade Polymarket, manage rooms, update read state, or take any other write action.
If you have a specific use case that would benefit from a broader API surface, let us know - that's the strongest signal for what to ship next.