Public reference for the Streamneeds REST API. Authentication is required per-request via an API key — generate one from your account's API Keys page.
All API requests require authentication using an API key. Include your API key in the Authorization header:
Authorization: Bearer bh_sk_your_api_key_here
https://streamneeds.com
readRead general data and leaderboards
writeWrite general data
points:readRead point balances and transactions
points:writeAward, remove, set, purchase, or refund points
users:readRead detailed user profiles (casinos, crypto, affiliate codes)
store:readRead store catalog items
store:purchaseRecord store purchases (can be used instead of points:write for checkout)
raffles:readRead raffle catalogue and a viewer's ticket totals
raffles:purchaseBuy raffle tickets on a viewer's behalf (can be used instead of points:write)
identity:writeLink/merge a viewer's platform accounts from your own site + ingest their credentials
games:readRead the slot database — the full game catalogue feed (GET /api/v1/games)
hunts:readRead this account's bonus hunts, summaries and per-hunt entries (GET /api/v1/hunts)
slot-requests:readRead this account's viewer slot requests (GET /api/v1/slot-requests)
adminLegacy scope — no longer grants implicit access to anything. Keys holding only 'admin' fail every check; re-issue them with explicit scopes.
/api/v1/healthZero-cost liveness probe. Returns 200 when the API is up — touches no database, requires no authentication, and can be polled as often as you like. Use THIS for availability pre-checks, never a data endpoint.
Required Scope:
public (no API key required)Response:
{
"ok": true,
"ts": 1752690000000
}Example cURL:
curl -X GET "https://streamneeds.com/api/v1/health" \ -H "Authorization: Bearer bh_sk_your_api_key_here"
/api/public/users/{identifier}/pointsGet a user's current point balance
Required Scope:
read or points:readParameters:
identifier•pathrequiredDiscord ID (17-20 digits), Kick username, or Viewer UUID
Response:
{
"viewerId": "uuid-here",
"points": 1500,
"identifier": "123456789012345678"
}Example cURL:
curl -X GET "https://streamneeds.com/api/public/users/123456789012345678/points" \ -H "Authorization: Bearer bh_sk_your_api_key_here"
/api/public/users/{identifier}/transactionsGet a user's point transaction history. Supports a since filter for reconciliation — fetch every movement after a timestamp and compare it against your own ledger.
Required Scope:
read or points:readParameters:
identifier•pathrequiredDiscord ID, Kick username, or Viewer UUID
limit•queryNumber of transactions to return (default: 50, max: 100)
offset•queryPagination offset (default: 0)
since•queryISO-8601 timestamp — only return movements at or after this instant (e.g. 2026-07-16T19:00:00Z)
Response:
{
"identifier": "username",
"transactions": [
{
"id": "tx-id",
"amount": 100,
"reason": "api_award",
"platform": "discord",
"createdAt": "2024-01-15T10:30:00Z"
}
],
"count": 1,
"limit": 50,
"offset": 0,
"since": "2024-01-15T00:00:00Z"
}Example cURL:
curl -X GET "https://streamneeds.com/api/public/users/123456789012345678/transactions" \ -H "Authorization: Bearer bh_sk_your_api_key_here"
/api/public/users/{identifier}/profileGet a user's complete profile: points, linked Discord/Kick accounts, casino credentials, crypto addresses, and affiliate codes scoped to the authenticated streamer.
Required Scope:
read or users:readParameters:
identifier•pathrequiredDiscord ID, Kick username, or Viewer UUID
Response:
{
"viewerId": "uuid-here",
"points": 1500,
"createdAt": "2024-01-01T00:00:00Z",
"discord": [
{
"discordId": "123456789012345678",
"username": "user",
"displayName": "Display Name",
"avatarUrl": "https://...",
"verified": true,
"banned": false,
"flagged": false
}
],
"kick": [
{
"username": "kickuser",
"displayName": "Kick Name",
"profilePicture": "https://...",
"verified": false
}
],
"casinos": [
{
"id": "uuid",
"platform": "stake",
"username": "casinoUser123",
"note": null,
"verified": true,
"createdAt": "2024-01-01T00:00:00Z",
"updatedAt": "2024-01-01T00:00:00Z"
},
{
"id": "uuid",
"platform": "hypebet",
"username": "hypeUser",
"note": "VIP",
"verified": false,
"createdAt": "2024-01-01T00:00:00Z",
"updatedAt": "2024-01-01T00:00:00Z"
}
],
"cryptoAddresses": [
{
"id": "uuid",
"currency": "USDT",
"address": "0x...",
"network": "ethereum",
"isPrimary": true,
"verified": true,
"createdAt": "2024-01-01T00:00:00Z",
"updatedAt": "2024-01-01T00:00:00Z"
}
],
"affiliateCodes": [
{
"id": "uuid",
"platform": "stake",
"code": "STREAMER123",
"verified": true,
"verifiedAt": "2024-01-02T00:00:00Z",
"createdAt": "2024-01-01T00:00:00Z",
"updatedAt": "2024-01-02T00:00:00Z"
}
]
}Example cURL:
curl -X GET "https://streamneeds.com/api/public/users/123456789012345678/profile" \ -H "Authorization: Bearer bh_sk_your_api_key_here"
/api/public/users/{identifier}/purchasesGet a user's store purchase history, scoped to the authenticated streamer's store. Supports filtering by purchase status and fulfillment status.
Required Scope:
read, users:read, or store:readParameters:
identifier•pathrequiredDiscord ID, Kick username, or Viewer UUID
limit•queryNumber of purchases to return (default: 50, max: 100)
offset•queryPagination offset (default: 0)
status•queryFilter by purchase status: completed, refunded, cancelled
fulfillmentStatus•queryFilter by fulfillment status: pending, processing, shipped, delivered, completed
Response:
{
"identifier": "username",
"viewerId": "uuid-here",
"purchases": [
{
"id": "purchase-uuid",
"item": {
"id": "item-uuid",
"name": "$10 Stake Bonus",
"slug": "stake-bonus-10",
"description": "Credited to your Stake account",
"imageUrl": "https://...",
"instructions": "Provide your Stake username at checkout"
},
"quantity": 1,
"pointsSpent": 750,
"pricePerItem": 750,
"status": "completed",
"fulfillmentStatus": "pending",
"paidStatus": "unpaid",
"fulfilledAt": null,
"refundedAt": null,
"refundReason": null,
"trackingNumber": null,
"notes": null,
"purchasedFrom": "api",
"createdAt": "2024-01-15T10:30:00Z",
"updatedAt": "2024-01-15T10:30:00Z"
}
],
"count": 1,
"total": 1,
"limit": 50,
"offset": 0
}Example cURL:
curl -X GET "https://streamneeds.com/api/public/users/123456789012345678/purchases" \ -H "Authorization: Bearer bh_sk_your_api_key_here"
/api/public/leaderboardGet top users by points
Required Scope:
read or points:readParameters:
limit•queryNumber of users to return (default: 10, max: 100)
offset•queryPagination offset (default: 0)
Response:
{
"leaderboard": [
{
"rank": 1,
"viewerId": "uuid-here",
"points": 5000,
"username": "TopUser",
"avatar": "https://...",
"verified": true,
"platforms": {
"discord": {
"discordId": "123456789012345678",
"username": "user#1234"
},
"kick": {
"username": "kickuser"
}
}
}
],
"count": 1,
"limit": 10,
"offset": 0
}Example cURL:
curl -X GET "https://streamneeds.com/api/public/leaderboard" \ -H "Authorization: Bearer bh_sk_your_api_key_here"
/api/public/points/awardAward points to a user. Supports idempotency keys: send a unique key per logical operation and retries with the same key replay the original result instead of awarding twice (replayed responses carry an Idempotency-Replay: true header).
Required Scope:
points:writeParameters:
identifier•bodyrequiredDiscord ID, Kick username, or Viewer UUID
amount•bodyrequiredNumber of points to award (must be positive)
reason•bodyReason for awarding points (default: 'api_award')
idempotencyKey•bodyUnique key (e.g. UUID) per logical operation — same key never awards twice. Can also be sent as an Idempotency-Key header. Keys expire after 7 days. Reusing a key with a different body returns 422; a concurrent duplicate returns 409 (retry shortly).
Request Body:
{
"identifier": "123456789012345678",
"amount": 100,
"reason": "tournament_win",
"idempotencyKey": "9f8e7d6c-5b4a-3210-fedc-ba9876543210"
}Response:
{
"success": true,
"viewerId": "uuid-here",
"identifier": "123456789012345678",
"awarded": 100,
"newBalance": 1600,
"reason": "tournament_win"
}Example cURL:
curl -X POST "https://streamneeds.com/api/public/points/award" \
-H "Authorization: Bearer bh_sk_your_api_key_here" \
-H "Content-Type: application/json" \
-d '{"identifier":"123456789012345678","amount":100,"reason":"tournament_win","idempotencyKey":"9f8e7d6c-5b4a-3210-fedc-ba9876543210"}'/api/public/points/removeRemove points from a user. Supports idempotency keys: send a unique key per logical operation and retries with the same key replay the original result instead of deducting twice (replayed responses carry an Idempotency-Replay: true header).
Required Scope:
points:writeParameters:
identifier•bodyrequiredDiscord ID, Kick username, or Viewer UUID
amount•bodyrequiredNumber of points to remove (must be positive)
reason•bodyReason for removing points (default: 'api_remove')
idempotencyKey•bodyUnique key (e.g. UUID) per logical operation — same key never deducts twice. Can also be sent as an Idempotency-Key header. Keys expire after 7 days. Reusing a key with a different body returns 422; a concurrent duplicate returns 409 (retry shortly).
Request Body:
{
"identifier": "kickusername",
"amount": 50,
"reason": "rule_violation",
"idempotencyKey": "1a2b3c4d-5e6f-7890-abcd-ef0123456789"
}Response:
{
"success": true,
"viewerId": "uuid-here",
"identifier": "kickusername",
"removed": 50,
"newBalance": 1550,
"reason": "rule_violation"
}Example cURL:
curl -X POST "https://streamneeds.com/api/public/points/remove" \
-H "Authorization: Bearer bh_sk_your_api_key_here" \
-H "Content-Type: application/json" \
-d '{"identifier":"kickusername","amount":50,"reason":"rule_violation","idempotencyKey":"1a2b3c4d-5e6f-7890-abcd-ef0123456789"}'/api/public/points/setSet a user's points to a specific amount
Required Scope:
points:writeParameters:
identifier•bodyrequiredDiscord ID, Kick username, or Viewer UUID
amount•bodyrequiredNew point balance (must be non-negative)
reason•bodyReason for setting points (default: 'api_set')
Request Body:
{
"identifier": "uuid-viewer-id",
"amount": 2000,
"reason": "season_reset"
}Response:
{
"success": true,
"viewerId": "uuid-viewer-id",
"identifier": "uuid-viewer-id",
"previousBalance": 1550,
"newBalance": 2000,
"reason": "season_reset"
}Example cURL:
curl -X POST "https://streamneeds.com/api/public/points/set" \
-H "Authorization: Bearer bh_sk_your_api_key_here" \
-H "Content-Type: application/json" \
-d '{"identifier":"uuid-viewer-id","amount":2000,"reason":"season_reset"}'/api/public/banned-games/{userId}List the games a streamer has banned from their bonus hunts. Public endpoint — no authentication.
Required Scope:
public (no API key required)Parameters:
userId•pathrequiredStreamer's user ID (their Streamneeds User UUID)
Response:
{
"user": {
"id": "streamer-uuid",
"name": "Streamer Name"
},
"bannedGames": [
{
"id": "uuid",
"gameName": "Example Slot",
"gameSlug": "example-slot",
"gameImage": "https://...",
"reason": "Low RTP / avoid",
"bannedBy": "streamer-uuid",
"createdAt": "2024-01-15T10:30:00Z"
}
],
"total": 1
}Example cURL:
curl -X GET "https://streamneeds.com/api/public/banned-games/{userId}" \
-H "Authorization: Bearer bh_sk_your_api_key_here"/api/public/store/itemsList the streamer's point-shop items (white-label store catalog). Calculates currentPrice / onSale from active sales.
Required Scope:
read or store:readParameters:
userId•queryrequiredMust match the API key's owner (self-scoped)
includeDisabled•queryInclude disabled items in the response (default: false)
Response:
{
"items": [
{
"id": "uuid",
"name": "$10 Stake Bonus",
"slug": "stake-bonus-10",
"description": "Credited to your Stake account",
"imageUrl": "https://...",
"price": 1000,
"salePrice": 750,
"saleEndsAt": "2024-02-01T00:00:00Z",
"stock": 50,
"sold": 12,
"enabled": true,
"maxPerUser": 1,
"instructions": "Provide your Stake username at checkout",
"currentPrice": 750,
"onSale": true,
"inStock": true,
"stockRemaining": 50,
"createdAt": "2024-01-01T00:00:00Z",
"updatedAt": "2024-01-20T00:00:00Z"
}
],
"total": 1
}Example cURL:
curl -X GET "https://streamneeds.com/api/public/store/items" \ -H "Authorization: Bearer bh_sk_your_api_key_here"
/api/public/store/purchaseRecord a purchase and deduct points. Supports Streamneeds store items (itemId) or external white-label items (amount + itemName). Optional webhookUrl fires a purchase.completed event.
Required Scope:
points:write or store:purchaseParameters:
identifier•bodyrequiredDiscord ID, Kick username, or Viewer UUID
itemId•bodyUUID of a Streamneeds store item. Mutually exclusive with amount + itemName.
quantity•bodyUnits to buy when using itemId (default: 1)
amount•bodyPoint cost for an external (non-Streamneeds) item
itemName•bodyDisplay name for an external item
itemDescription•bodyDescription for the external item
metadata•bodyArbitrary JSON metadata passed through to the webhook
storeId•bodyExternal store identifier
storeName•bodyExternal store display name
externalTxId•bodyYour external transaction ID for reconciliation
webhookUrl•bodyURL that receives a fire-and-forget purchase.completed POST on success
Request Body:
{
"identifier": "discord:123456789012345678",
"itemId": "uuid-of-store-item",
"quantity": 1,
"webhookUrl": "https://your-site.com/webhooks/purchase"
}Response:
{
"success": true,
"transaction": {
"purchaseId": "uuid",
"viewerId": "uuid-viewer-id",
"previousBalance": 2000,
"amountDeducted": 750,
"newBalance": 1250,
"itemName": "$10 Stake Bonus",
"itemDescription": "Credited to your Stake account",
"quantity": 1,
"instructions": "Provide your Stake username at checkout",
"timestamp": "2024-01-20T12:30:00Z"
}
}Example cURL:
curl -X POST "https://streamneeds.com/api/public/store/purchase" \
-H "Authorization: Bearer bh_sk_your_api_key_here" \
-H "Content-Type: application/json" \
-d '{"identifier":"discord:123456789012345678","itemId":"uuid-of-store-item","quantity":1,"webhookUrl":"https://your-site.com/webhooks/purchase"}'/api/public/rafflesList the streamer's raffles with ticket price, draw date, totals, and winner info. Use this to render a raffle catalogue on your site.
Required Scope:
read or raffles:readParameters:
status•queryFilter by status: active | drawn | cancelled | all (default: active)
includeDisabled•queryInclude raffles with enabled=false (default: false)
Response:
{
"raffles": [
{
"id": "uuid",
"slug": "weekly-cashout",
"name": "Weekly $100 Cashout",
"description": "Cash prize drawn every Friday",
"imageUrl": "https://...",
"ticketPrice": 50,
"status": "drawn",
"enabled": true,
"drawDate": "2024-02-02T20:00:00Z",
"drawnAt": "2024-02-02T20:05:00Z",
"isOpen": false,
"stats": {
"totalTickets": 240,
"totalEntries": 47,
"entryRecords": 62
},
"winner": {
"viewerId": "uuid-viewer-id",
"username": "LuckyDuck",
"winningTicketNumber": 137,
"ticketsEntered": 18,
"drawnAt": "2024-02-02T20:05:00Z"
},
"createdAt": "2024-01-20T00:00:00Z",
"updatedAt": "2024-01-20T00:00:00Z"
}
],
"total": 1
}Example cURL:
curl -X GET "https://streamneeds.com/api/public/raffles" \ -H "Authorization: Bearer bh_sk_your_api_key_here"
/api/public/raffles/{id}Get a single raffle by UUID or slug. Pass an optional identifier to get that viewer's ticket totals for this raffle. Pass includeTicketNumbers=true alongside identifier to include the viewer's individual ticket numbers.
Required Scope:
read or raffles:readParameters:
id•pathrequiredRaffle UUID or slug
identifier•queryDiscord ID / Kick username / Viewer UUID — returns that viewer's ticketCount + pointsSpent + isWinner under `viewer`
includeTicketNumbers•querySet to 'true' alongside `identifier` to include that viewer's individual ticket numbers in `viewer.ticketNumbers`. Default: false.
Response:
{
"id": "uuid",
"slug": "weekly-cashout",
"name": "Weekly $100 Cashout",
"description": "Cash prize drawn every Friday",
"imageUrl": "https://...",
"ticketPrice": 50,
"status": "drawn",
"enabled": true,
"drawDate": "2024-02-02T20:00:00Z",
"drawnAt": "2024-02-02T20:05:00Z",
"isOpen": false,
"stats": {
"totalTickets": 240,
"totalEntries": 47,
"entryRecords": 62
},
"winner": {
"viewerId": "uuid-viewer-id",
"username": "LuckyDuck",
"winningTicketNumber": 137,
"ticketsEntered": 18,
"drawnAt": "2024-02-02T20:05:00Z"
},
"viewer": {
"viewerId": "uuid-viewer-id",
"ticketCount": 12,
"pointsSpent": 600,
"isWinner": false,
"ticketNumbers": [
3,
4,
5,
88,
89,
90,
91,
92,
150,
151,
152,
153
]
},
"createdAt": "2024-01-20T00:00:00Z",
"updatedAt": "2024-01-20T00:00:00Z"
}Example cURL:
curl -X GET "https://streamneeds.com/api/public/raffles/{id}" \
-H "Authorization: Bearer bh_sk_your_api_key_here"/api/public/raffles/purchaseBuy raffle tickets on behalf of a viewer. Deducts points atomically and increments the raffle's totals. ticketCount is capped at 10,000 per call.
Required Scope:
points:write or raffles:purchaseParameters:
identifier•bodyrequiredDiscord ID, Kick username, or Viewer UUID
prizeId•bodyrequiredRaffle UUID or slug
ticketCount•body1-10000 (default: 1)
purchasedFrom•bodySource label stored on the entry (default: 'api')
Request Body:
{
"identifier": "discord:123456789012345678",
"prizeId": "weekly-cashout",
"ticketCount": 5
}Response:
{
"success": true,
"transaction": {
"entryId": "uuid",
"viewerId": "uuid-viewer-id",
"prizeId": "uuid",
"prizeName": "Weekly $100 Cashout",
"ticketCount": 5,
"pricePerTicket": 50,
"pointsSpent": 250,
"previousBalance": 2000,
"newBalance": 1750,
"ticketNumbers": {
"first": 241,
"last": 245
},
"viewerTotalsForRaffle": {
"ticketCount": 12,
"pointsSpent": 600
},
"timestamp": "2024-01-25T12:30:00Z"
}
}Example cURL:
curl -X POST "https://streamneeds.com/api/public/raffles/purchase" \
-H "Authorization: Bearer bh_sk_your_api_key_here" \
-H "Content-Type: application/json" \
-d '{"identifier":"discord:123456789012345678","prizeId":"weekly-cashout","ticketCount":5}'/api/public/users/{identifier}/rafflesList a viewer's raffle tickets, grouped by prize. Each group includes the individual ticket numbers they hold (for a 'my tickets' page or post-draw reveal), the total tickets they bought, and whether they won the prize.
Required Scope:
read, users:read, or raffles:readParameters:
identifier•pathrequiredDiscord ID, Kick username, or Viewer UUID
prizeId•queryFilter to a single raffle
status•queryFilter prizes by status: active, drawn, cancelled
includeTicketNumbers•querySet to "false" to omit the per-ticket number list (useful for large raffles). Default: true
Response:
{
"identifier": "username",
"viewerId": "uuid-viewer-id",
"raffles": [
{
"prize": {
"id": "uuid",
"name": "Weekly $100 Cashout",
"slug": "weekly-cashout",
"imageUrl": "https://...",
"ticketPrice": 50,
"drawDate": "2024-02-02T20:00:00Z",
"status": "active",
"totalTickets": 240
},
"ticketCount": 5,
"pointsSpent": 250,
"ticketNumbers": [
241,
242,
243,
244,
245
],
"isWinner": false,
"winningTicketNumber": null
}
],
"totalRaffles": 1,
"totalTickets": 5
}Example cURL:
curl -X GET "https://streamneeds.com/api/public/users/123456789012345678/raffles" \ -H "Authorization: Bearer bh_sk_your_api_key_here"
/api/public/store/refundCredit points back to a viewer (use when a purchase fails, an item is out of stock, or you need to reverse a transaction). Logged to the streamer's activity log.
Required Scope:
points:writeParameters:
identifier•bodyrequiredDiscord ID, Kick username, or Viewer UUID
amount•bodyrequiredPoints to credit back (must be positive)
reason•bodyrequiredHuman-readable reason for the refund
externalTxId•bodyYour external transaction ID for the refund
originalTxId•bodyThe original purchase transaction you're reversing
Request Body:
{
"identifier": "discord:123456789012345678",
"amount": 500,
"reason": "Purchase failed - item out of stock",
"externalTxId": "tx_refund_abc123",
"originalTxId": "tx_original_purchase"
}Response:
{
"success": true,
"viewerId": "uuid-viewer-id",
"amountRefunded": 500,
"newBalance": 1750,
"reason": "Purchase failed - item out of stock"
}Example cURL:
curl -X POST "https://streamneeds.com/api/public/store/refund" \
-H "Authorization: Bearer bh_sk_your_api_key_here" \
-H "Content-Type: application/json" \
-d '{"identifier":"discord:123456789012345678","amount":500,"reason":"Purchase failed - item out of stock","externalTxId":"tx_refund_abc123","originalTxId":"tx_original_purchase"}'/api/v1/identity/linkExternal identity-link / merge request. For streamers running their OWN site where viewers log in and link their platforms. Send all the linked ids in one call and we merge them into a single unified viewer, ingesting casino/crypto/affiliate credentials. An account that hasn't chatted yet is pre-seeded and auto-attaches to the same viewer on its first appearance in chat — no follow-up call needed. Idempotent: re-send the same body to force a re-merge immediately (also honours an Idempotency-Key header). At least one of discordId / kickUserId is required.
Required Scope:
identity:writeParameters:
externalId•bodyrequiredYour stable id for the user (e.g. Google sub or your own DB id). One link record per externalId; re-POST to update.
discordId•bodyDiscord snowflake (from Discord OAuth). Required unless kickUserId is given.
kickUserId•bodyKick's NUMERIC user id (from Kick OAuth) — not the username. Non-numeric values are rejected with 400. Required unless discordId is given.
autoVerify•bodyMark the linked Discord account + ingested credentials verified (the site login is the vouch). Default: true.
credentials•bodyOptional { casino: [{platform, username}], crypto: [{currency, address, network}], affiliate: [{platform, code}] } — all upserted onto the viewer.
idempotencyKey•bodyUnique key per logical operation (or Idempotency-Key header) — safe retries. The whole operation is idempotent regardless.
Request Body:
{
"externalId": "google-oauth-sub-123",
"discordId": "123456789012345678",
"kickUserId": "987654",
"autoVerify": true,
"credentials": {
"casino": [
{
"platform": "hypebet",
"username": "bigwinner"
}
],
"affiliate": [
{
"platform": "hypebet",
"code": "HADDZY"
}
]
}
}Response:
{
"success": true,
"viewerId": "uuid-viewer-id",
"createdViewer": false,
"mergedViewerCount": 1,
"discordLinked": true,
"kickLinked": false,
"discordPending": false,
"kickPending": true,
"credentialsApplied": {
"casino": 1,
"crypto": 0,
"affiliate": 1
},
"fullyApplied": false
}Example cURL:
curl -X POST "https://streamneeds.com/api/v1/identity/link" \
-H "Authorization: Bearer bh_sk_your_api_key_here" \
-H "Content-Type: application/json" \
-d '{"externalId":"google-oauth-sub-123","discordId":"123456789012345678","kickUserId":"987654","autoVerify":true,"credentials":{"casino":[{"platform":"hypebet","username":"bigwinner"}],"affiliate":[{"platform":"hypebet","code":"HADDZY"}]}}'/api/v1/identity/linkStatus check for a link you already pushed — confirm the merge fully landed and see which side (if any) is still pending because that account hasn't appeared in chat yet. Returns 404 with { found: false } if no link exists for that externalId under this streamer.
Required Scope:
identity:writeParameters:
externalId•queryrequiredThe externalId you sent when creating the link.
Response:
{
"found": true,
"externalId": "google-oauth-sub-123",
"viewerId": "uuid-viewer-id",
"fullyApplied": false,
"discord": {
"supplied": true,
"linked": true,
"pending": false
},
"kick": {
"supplied": true,
"linked": false,
"pending": true
},
"autoVerify": true,
"appliedAt": null,
"updatedAt": "2026-07-17T12:00:00Z"
}Example cURL:
curl -X GET "https://streamneeds.com/api/v1/identity/link" \ -H "Authorization: Bearer bh_sk_your_api_key_here"
/api/v1/gamesThe slot database — the full game catalogue. Ordered by (updatedAt, id) ascending, so a since watermark plus cursor walks it deterministically. This catalogue is shared platform-wide, not per-account: every key with games:read sees the same rows. Store the highest updatedAt you have seen and pass it as since on the next run; do not re-drain from scratch on a schedule.
Required Scope:
games:readParameters:
since•queryISO-8601 timestamp — only games with updatedAt at or after this (incremental sync)
cursor•queryOpaque keyset cursor from the previous page's nextCursor
limit•queryPage size (default: 500, max: 1000)
Response:
{
"games": [
{
"slug": "sweet-bonanza",
"name": "Sweet Bonanza",
"provider": "Pragmatic Play",
"imageUrl": "https://.../sweet-bonanza.png",
"rtp": 96.51,
"volatility": "High",
"maxWin": "21,100x",
"betRange": "0.20 - 125",
"releaseDate": "2019-06-27T00:00:00Z",
"releaseStatus": "released",
"reels": 6,
"rows": 5,
"paylines": "Pays Anywhere",
"hitFreq": "24.5%",
"features": [
"Free Spins",
"Tumble",
"Multiplier"
],
"timesUsedInHunts": 412,
"rating": 8.5,
"updatedAt": "2026-08-01T09:12:00Z"
}
],
"nextCursor": "MjAyNi0wOC0wMVQwOToxMjowMFp8...",
"count": 1
}Example cURL:
curl -X GET "https://streamneeds.com/api/v1/games" \ -H "Authorization: Bearer bh_sk_your_api_key_here"
/api/v1/huntsBonus hunt summaries, newest first (no entries — use the detail endpoint for those). Scoped to the key owner's own hunts; admin keys read across all streamers. Add anonymize=1 to replace every streamer/viewer identity with a stable pseudonym.
Required Scope:
hunts:readParameters:
since•queryISO-8601 timestamp — only hunts created at or after this
cursor•queryOpaque keyset cursor from the previous page's nextCursor
limit•queryPage size (default: 50, max: 200)
status•querypreparing | live | completed
anonymize•querySet to 1 to replace names with a stable 12-hex pseudonym (anonId)
streamer•queryAdmin keys only — restrict to one streamer's User.id. Ignored on non-admin keys.
Response:
{
"hunts": [
{
"id": "uuid-hunt-id",
"title": "Sunday Hunt",
"casino": "Hype.bet",
"currency": "USD",
"status": "completed",
"startBalance": 5000,
"endBalance": 8200,
"totalCost": 4000,
"totalWon": 8200,
"profit": 4200,
"entryCount": 20,
"isPublic": false,
"shareSlug": "8kz0jhh1",
"startedAt": "2026-08-01T19:00:00Z",
"completedAt": "2026-08-01T22:30:00Z",
"createdAt": "2026-08-01T18:45:00Z",
"streamer": {
"userId": "uuid-user-id",
"name": "Streamer",
"username": "streamer",
"image": null,
"kickUsername": "streamer"
}
}
],
"nextCursor": "MjAyNi0wOC0wMVQxODo0NTowMFp8...",
"count": 1
}Example cURL:
curl -X GET "https://streamneeds.com/api/v1/hunts" \ -H "Authorization: Bearer bh_sk_your_api_key_here"
/api/v1/hunts/{id}Full detail for one bonus hunt — every summary field plus each entry in play order. A hunt belonging to another account returns 404 (not 403), so the endpoint never confirms a foreign hunt exists.
Required Scope:
hunts:readParameters:
id•pathrequiredThe hunt's UUID, from the /api/v1/hunts list
anonymize•querySet to 1 to replace names with a stable 12-hex pseudonym (anonId)
Response:
{
"hunt": {
"id": "uuid-hunt-id",
"title": "Sunday Hunt",
"status": "completed",
"startBalance": 5000,
"totalCost": 4000,
"totalWon": 8200,
"profit": 4200,
"entries": [
{
"id": "uuid-entry-id",
"gameName": "Sweet Bonanza",
"gameSlug": "sweet-bonanza",
"gameProvider": "Pragmatic Play",
"betSize": 2,
"cost": 200,
"result": 1450,
"multiplier": 725,
"position": 1,
"superBonus": false,
"status": "completed"
}
]
}
}Example cURL:
curl -X GET "https://streamneeds.com/api/v1/hunts/{id}" \
-H "Authorization: Bearer bh_sk_your_api_key_here"/api/v1/slot-requestsViewer slot requests, most-recently-updated first. Granted separately from hunts:read so a hunt consumer does not automatically gain viewer request data. Scoped to the key owner's own queue; admin keys read across all streamers. since filters on updatedAt, which makes this feed genuinely incremental.
Required Scope:
slot-requests:readParameters:
since•queryISO-8601 timestamp — only requests updated at or after this
cursor•queryOpaque keyset cursor from the previous page's nextCursor
limit•queryPage size (default: 100, max: 500)
status•querypending | played | skipped | archived
anonymize•querySet to 1 to replace requester identities with a stable pseudonym
streamer•queryAdmin keys only — restrict to one streamer's User.id. Ignored on non-admin keys.
Response:
{
"slotRequests": [
{
"id": "uuid-request-id",
"streamerUserId": "uuid-user-id",
"gameName": "Sweet Bonanza",
"gameSlug": "sweet-bonanza",
"status": "played",
"isSuper": false,
"voteCount": 12,
"huntId": "uuid-hunt-id",
"betSize": 2,
"winAmount": 1450,
"multiplier": 725,
"pointsAwarded": 500,
"requester": {
"platform": "kick",
"username": "viewer",
"displayName": "Viewer",
"avatar": null
},
"playedAt": "2026-08-01T20:14:00Z",
"createdAt": "2026-08-01T19:02:00Z",
"updatedAt": "2026-08-01T20:14:00Z"
}
],
"nextCursor": "MjAyNi0wOC0wMVQyMDoxNDowMFp8...",
"count": 1
}Example cURL:
curl -X GET "https://streamneeds.com/api/v1/slot-requests" \ -H "Authorization: Bearer bh_sk_your_api_key_here"
{
"error": "Invalid or missing API key"
}{
"error": "API key does not have 'points:write' scope"
}{
"error": "User not found"
}{
"error": "amount must be a positive number"
}{
"error": "Rate limit exceeded",
"retryAfter": 2
}{
"error": "Internal server error"
}{
"error": "Service temporarily overloaded, retry shortly",
"retryAfter": 2
}Health checks go to /api/v1/health
If your integration pre-checks availability before sending a transaction, probe /api/v1/health — it costs nothing on our side. Never use a data endpoint (like the points balance) as a liveness probe: those run real database queries, and probing them at volume degrades the service for everyone, including you.
Retry with exponential backoff
On a 500, 429, or timeout, wait before retrying — 2s, then 4s, then 8s (with a retry cap). Instant re-sends amplify load exactly when the service is under pressure and make recovery slower for everyone.
Use idempotency keys on point mutations
A timed-out points/award or points/remove call may have succeeded server-side, so a blind re-send risks double-applying it. Send a unique idempotencyKey (body field or Idempotency-Key header) with every mutation — then retries are always safe: if the first attempt landed, you get the original result back instead of a second application. Without a key, verify the balance before re-sending.
Respect rate limits
Requests are rate-limited per API key. The /api/v1 read feeds (games, hunts, slot-requests) allow 120 requests per minute per key. A 429 response includes Retry-After (seconds) plus X-RateLimit-Limit, -Remaining and -Reset — honour them and back off rather than retrying hot.
Those feeds are keyset-paginated and support a since watermark, so a normal incremental poll costs one or two requests. If you are approaching the limit you are almost certainly re-draining history instead of using since — store the highest updatedAt you have seen and resume from it.
The API supports three types of user identifiers. The system will automatically detect the type:
Discord ID (Snowflake)
17-20 digit numeric ID
123456789012345678Kick Username
String username from Kick platform
kickusernameViewer UUID
Unified viewer identifier (internal)
a1b2c3d4-e5f6-7890-abcd-ef1234567890