Presence API
Use the Nowly Presence API to update Discord Rich Presence from a website integration.
Creating a Presence
Presence is a global injected by the Nowly runtime - you don't need to import it.
Every presence starts with a Presence instance:
const presence = new Presence()UpdateData Event
Use UpdateData to refresh activity:
presence.on("UpdateData", async () => {
await presence.setActivity({
details: "Watching a video",
})
})setActivity
setActivity sends the current Discord activity. Common fields include:
| Field | Type | Description | Default |
|---|---|---|---|
detailsUrl | string | Optional public HTTPS link for details (SDK 1.7.0+). | - |
stateUrl | string | Optional public HTTPS link for state (SDK 1.7.0+). | - |
name | string | Optional activity name. Defaults to the presence name. | - |
statusDisplayType | 0 | 1 | 2 | Field shown in Discord's compact status: 0 = name (default), 1 = state, 2 = details. | - |
details | string | Primary activity line shown in Discord. | - |
state | string | Secondary activity line shown below details. | - |
startTimestamp | number | Unix timestamp for elapsed activity progress. | - |
endTimestamp | number | Unix timestamp for remaining activity progress. | - |
largeImageKey | string | Main image asset key, usually Assets.Logo or Assets.Thumbnail (globals provided by the runtime). | - |
largeImageText | string | Tooltip for the large image. | - |
smallImageKey | string | Optional small image asset key. | - |
smallImageText | string | Tooltip for the small image. | - |
type | number | Discord activity type from PresenceType. | - |
buttons | array | Optional Discord activity buttons. | - |
Discord rejects Rich Presence payloads when largeImageText or smallImageText contains fewer than two characters. Nowly trims these optional tooltips and omits them when the result is shorter than two characters; the image remains available.
detailsUrl / stateUrl
SDK 1.7.0+ adds optional string fields detailsUrl and stateUrl to make the corresponding activity lines clickable, independently of buttons. Both the extension and native host must support them; older runtimes ignore them. The activity name is not affected.
Use verified public HTTPS URLs, without credentials and no longer than 2048 characters. Links without corresponding text or with invalid URLs are omitted. Keep content identifiers such as YouTube's v query parameter, but never expose access tokens, private chats, secret links, or information hidden by privacy settings. Omit a link when its destination is unknown.
await presence.setActivity({
details: "Video title",
detailsUrl: "https://www.youtube.com/watch?v=VIDEO_ID",
state: "Channel name",
stateUrl: "https://www.youtube.com/@channel",
})statusDisplayType
statusDisplayType chooses the text shown in Discord's compact status. It does not change the activity card's name, details, or state.
0(default):name1:state2:details
For example, show the video title in the compact status while keeping the activity name and channel in the card:
await presence.setActivity({
name: "Video service",
details: "Video title",
state: "Channel name",
statusDisplayType: 2,
})Use an SDK version that includes this field and update both the Nowly extension and native host. Older runtime versions ignore the field and keep the default status display.
clearActivity
Call clearActivity when there is nothing worth showing:
presence.clearActivity()PresenceType
PresenceType is a named export from @nowly/sdk:
import { PresenceType } from "@nowly/sdk"It maps to Discord activity types: Playing, Streaming, Listening, Watching, and Competing.
getSetting
Read a typed per-presence setting after Presence.Settings. The value is the user's saved setting, or the definition default:
const showButtons = await presence.getSetting<boolean>("showButtons")UpdateData also receives ctx.settings with the same map. Both update when the user changes settings in the side panel.
Logging
Use presence.info and presence.error for debug messages during development.