Api Reference
Api Reference
API Reference
[Overview](./)
Public API only
The examples describe the client-facing API. All requested users must explicitly approve presence sharing.
This reference covers the public SDK and hosted endpoints. It describes what an application can request and how to handle a response; it does not require access to the hosted service implementation.
API origin and authentication
The SDK uses https://presence-api.iconical.dev and intentionally does not accept a baseUrl override. Consumers do not include a Discord bot token or OAuth credential. Instead, PresenceKit verifies each user's permission to share server-side.
REST endpoints
| Method | Endpoint | Description |
|---|---|---|
| GET | /health | Basic service health |
| GET | /v1/presence/:discordUserId | One approved user snapshot |
| GET | /v1/presences?ids=id1,id2 | Up to 25 approved snapshots |
| GET | /v1/presence/:discordUserId/events | Single-user SSE stream |
| GET | /v1/presences/events?ids=id1,id2 | Multi-user SSE stream |
Example request (replace the ID):
GET /v1/presence/123456789012345678 HTTP/1.1
Host: presence-api.iconical.devSnapshot response
{
"ok": true,
"availability": "live",
"profile": {
"id": "123456789012345678",
"username": "example",
"displayName": "Example Developer",
"avatarUrl": "https://cdn.discordapp.com/embed/avatars/0.png",
"avatarDecorationUrl": null,
"bannerUrl": null
},
"presence": {
"userId": "123456789012345678",
"status": "online",
"devices": { "desktop": "online" },
"updatedAt": "2026-10-17T12:00:00.000Z",
"activities": []
},
"spotify": null
}This is an illustrative response, not a live account. Additional optional profile/activity fields may be supplied. availability is either live or pending; when pending, presence is null even though the user is approved.
| Field | Type | Meaning | |
|---|---|---|---|
ok | true | Successful approved response | |
availability | `"live" \ | "pending"` | Whether a fresh activity snapshot is available |
profile | DiscordProfile | Approved public-facing profile fields | |
presence | `PresenceSnapshot \ | null` | Status, device indicators, timestamp and activities |
spotify | `SpotifyNowPlaying \ | null` | Convenience metadata when Spotify activity is visible |
/v1/presence/:discordUserIdReturn the latest approved profile and presence snapshot for one user.
Request
{"discordUserId":"123456789012345678"}Response (200)
{"ok":true,"availability":"pending","profile":{"id":"123456789012345678","username":"example","displayName":"Example","avatarUrl":"https://cdn.discordapp.com/embed/avatars/0.png","avatarDecorationUrl":null,"bannerUrl":null},"presence":null,"spotify":null}Create a client
import { createPresenceClient } from "@iconical/presence";
const client = createPresenceClient();
const result = await client.getPresence("123456789012345678");getPresence(userId, { signal? })
Returns Promise. The optional AbortSignal cancels the HTTP request.
const controller = new AbortController();
const result = await client.getPresence("123456789012345678", {
signal: controller.signal,
});subscribePresence(userId, observer, options?)
Returns a cleanup function () => void. observer.onUpdate receives a complete PresenceResult and optional observer.onError receives an Error.
const stop = client.subscribePresence(
"123456789012345678",
{ onUpdate: (next) => console.log(next.availability) },
{ transport: "auto", intervalMs: 15_000 },
);
// When no longer needed:
stop();| Option | Values / behavior |
|---|---|
transport | "auto" (default), "sse", or "poll" |
intervalMs | REST polling interval; default 15,000 ms, minimum 5,000 ms |
onTransportChange | Optional callback: connecting, sse, poll, stopped |
With auto, the browser prefers SSE and falls back to polling on repeated failures or an initial-event timeout. Explicit sse does not silently switch to polling. Environments without EventSource can use polling; requesting explicit sse there throws an error.
getPresences(ids, { signal? })
Returns Promise with presences: Record. Batch results omit unapproved IDs; do not expect every requested ID to appear. Low-level requests accept 1–25 unique valid Discord IDs.
subscribePresences(ids, observer, options?)
Opens one SSE subscription for 1–25 requested IDs, or one batched REST polling flow. Returns a cleanup function. See Multi-user for code examples and revocation behavior.
Activity helpers
import {
getSpotifyActivity,
getSpotifyNowPlaying,
getPlayingActivities,
getCustomStatus,
getActivityArtwork,
getActivityElapsedMs,
} from "@iconical/presence";
const spotifyActivity = getSpotifyActivity(result.presence);
const track = getSpotifyNowPlaying(result.presence);
const games = getPlayingActivities(result.presence);
const custom = getCustomStatus(result.presence);
const art = getActivityArtwork(games[0]);
const elapsed = getActivityElapsedMs(games[0]);The helpers accept nullable/missing activity data where indicated and return null or an empty list when no matching activity exists. getActivityArtwork() also accepts an optional { overrides } map keyed by Discord application ID.
Live SSE events
| Stream | Event | Meaning |
|---|---|---|
| Single-user | presence | Initial or changed complete PresenceResult |
| Single-user | revoked | Permission ended; close/remove the data |
| Single-user | unavailable | Temporary service interruption |
| Multi-user | snapshot | Initial consent-filtered map |
| Multi-user | presence | Changed approved member |
| Multi-user | revoked | Remove one member from the map |
| Multi-user | unavailable | Temporary member or service interruption |
EventSource also receives comment heartbeats. The SDK handles event parsing and reconnection automatically; application code should normally use its subscription methods rather than parsing SSE manually.
HTTP errors
| Status | Meaning | Suggested UI behavior |
|---|---|---|
400 | Invalid parameters, such as malformed batch IDs | Correct the input |
404 | User data unavailable, not approved, or no qualifying batch members | Show unavailable without guessing why |
405 | Unsupported method | Use documented GET endpoints |
429 | Rate or stream limit reached | Back off; respect Retry-After when provided |
503 | Service or Gateway temporarily unavailable | Show temporary interruption; retry conservatively |
The SDK throws PresenceKitError for HTTP errors. It exposes status (number) and code (string):
import { PresenceKitError } from "@iconical/presence";
try {
await client.getPresence("123456789012345678");
} catch (error) {
if (error instanceof PresenceKitError && error.status === 404) {
console.log("Presence unavailable");
}
}Discord activity types
| Value | Activity |
|---|---|
0 | Playing |
1 | Streaming |
2 | Listening |
3 | Watching |
4 | Custom status |
5 | Competing |
A snapshot is current state, not a history. Join/spectate secrets, private messages and raw Gateway session IDs are excluded from the public response. Read Privacy before storing or redistributing returned data.
In this section
Was this page helpful?