Starwide API docs

The Starwide API is a JSON REST API built on WordPress, BuddyPress and bbPress. Use it from JavaScript in the browser, from your own server, or from a game.

Base URL https://www.starwide.net/wp-json/starwide/v1

Quick start

  1. Create an app under Apps. You get a Title Token and a Secret Key.
  2. In the app console, generate a bearer token and copy it straight away. It's shown once.
  3. Call /apps/verify to check everything is wired up:
javascript
const BEARER = 'your-40-character-bearer-token';
const TITLE_TOKEN = 'your-16-char-title';

const res = await fetch('https://www.starwide.net/wp-json/starwide/v1/apps/verify', {
  headers: {
    Authorization: `Bearer ${BEARER}`,
    'X-Starwide-App': TITLE_TOKEN,
  },
});
if (!res.ok) throw new Error((await res.json()).message);
console.log(await res.json()); // { ok: true, app: {...}, token: {...} }
bash
curl https://www.starwide.net/wp-json/starwide/v1/apps/verify \
  -H "Authorization: Bearer $BEARER" \
  -H "X-Starwide-App: $TITLE_TOKEN"

Credentials

CredentialFormatWhere it goesCan you see it again?
Title Token16 characters: letters, numbers, symbolsX-Starwide-AppYes, any time. It isn't secret.
Secret Key20 characters: letters, numbers, symbolsX-Starwide-Secret (server only)Yes, from your console. Never share it.
Bearer token40 hex charactersAuthorization: Bearer …No. Shown once; only a hash is stored.

Authentication

Every call to /apps/* (except the public directory) needs your bearer token and Title Token. The bearer token must be attached to the app the Title Token names.

http
GET /wp-json/starwide/v1/apps/users/neff
Authorization: Bearer 3f9a…c21e
X-Starwide-App: Kd7_mP2!xQ9.vR4z

Some hosts strip the Authorization header. If you get "auth missing" errors, send X-Starwide-Bearer: <token> instead.

Bearer tokens may be used in browser code and shared with collaborators who build on your app. Your Secret Key may not: keep it on your server.

Endpoints

GET/apps/verifyBearer

Checks your credentials and returns your app and token hint.

GET/apps/users/{username}Bearer

A member's public profile: display name, avatar, bio, links, follower count, whether they're a developer. Private profiles return only name and avatar. {username} can also be a numeric ID.

json
{
  "id": 1,
  "username": "neff",
  "display_name": "Nepheon",
  "avatar_url": "https://…",
  "profile_url": "https://starwide.net/members/neff/",
  "private": false,
  "bio": "…",
  "links": [{ "label": "YouTube", "url": "https://…" }],
  "follower_count": 1280,
  "member_since": "2020-04-01T12:00:00",
  "is_developer": true,
  "apps_url": "https://starwide.net/members/neff/apps/"
}
GET/apps/directory?developer={username}&per_page=20Public

Published and Early Access apps. Public: no token needed.

Sign in with Starwide

Let members log in to your app with their Starwide account and share data with it. It's standard OAuth 2.0 (authorization code), so most OAuth libraries work.

  1. Set your app's Callback URL in the console (https, or http://localhost while testing).
  2. Send the member to the authorize page with the scopes you need:
url
https://www.starwide.net/for-developers/authorize/?client_id=TITLE_TOKEN
  &redirect_uri=https://yourapp.example/callback
  &response_type=code
  &scope=followers feed.read wallet.read
  &state=RANDOM_VALUE
  &code_challenge=BASE64URL_SHA256_OF_VERIFIER   (browser & mobile apps)
  &code_challenge_method=S256

When they click Allow, they come back to your Callback URL with ?code=…&state=…. Check state matches, then swap the code (valid 10 minutes, once) for tokens:

javascript
// Server apps: send your Secret Key. Browser/mobile apps: send code_verifier instead.
const res = await fetch('https://www.starwide.net/wp-json/starwide/v1/oauth/token', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    grant_type: 'authorization_code',
    client_id: TITLE_TOKEN,
    code,
    redirect_uri: 'https://yourapp.example/callback',
    client_secret: SECRET_KEY,      // or: code_verifier: VERIFIER
  }),
});
const { access_token, refresh_token, expires_in, scope, user } = await res.json();
  • Access tokens (swu_…) last 1 hour. Refresh tokens (swr_…) last 60 days and change every time you use one: POST grant_type=refresh_token with client_id, refresh_token and (server apps) client_secret.
  • Revoke a token with POST /oauth/revoke and token=….
  • Members who already approved the same permissions skip the screen. Add prompt=consent to always show it.
  • AO / NSFW apps can only be connected by members who are 18 or older.
ScopeGives your app
profile (always)Your profile, username and e-mail address. Display name, avatar, bio, bio links and the Pages you belong to.
followersWho you follow and who follows you. Your followers and following lists.
feed.readYour feed history. Posts and updates you've shared.
feed.writePost to your feed. Posts are labelled with the app's name.
media.readYour public uploads. Videos, images and music you've made public.
tokens.readYour bearer token inventory. Which tokens you own. Never the token values.
wallet.readYour wallet balance. The amount only. Never your cards, bank or payment methods.
purchases.readYour purchase history. Items, prices, licence ownership and subscriptions. Adult purchases and payment details are always hidden.
arcade.writePost your scores and achievements. Shown on the game's leaderboard under your name.
comments.writeComment and pin moments on media for you. Timestamped comments on videos and music, labelled with the app's name.
feed.syndicateReceive posts you choose to share with it. Only public posts, and only when you pick "Connected apps" under "Also post to".
paymentsAsk you to pay for things. You confirm every payment on Starwide. It can never charge you on its own.

Member endpoints

Send the member's access token and your Title Token:

http
GET /wp-json/starwide/v1/apps/me
Authorization: Bearer swu_…
X-Starwide-App: TITLE_TOKEN
GET/apps/meBearer

Profile, username, e-mail and Pages. Scope: profile.

GET/apps/me/followers · /apps/me/followingBearer

Lists of members (?page, ?per_page up to 100). Scope: followers.

GET/apps/me/feedBearer

The member's posts. NSFW posts only reach AO-rated apps, for adult members. Scope: feed.read.

POST/apps/me/feedBearer

Posts an update ({"content": "…"}), shown as "via your app". Scope: feed.write.

GET/apps/me/mediaBearer

Public Encore uploads. Scope: media.read.

GET/apps/me/tokensBearer

Bearer tokens they own, never the values. Scope: tokens.read.

GET/apps/me/walletBearer

Balance and a top-up link. Nothing else. Scope: wallet.read.

GET/apps/me/purchasesBearer

Orders with items and prices. NSFW items removed and totals recalculated; no payment details. Scope: purchases.read.

Missing a permission? You'll get 403 insufficient_scope. Expired token? 401 invalid_token — refresh it.

Arcade leaderboards

Submit scores and achievements for signed-in members (scope arcade.write). Each member's best score counts. Boards are any name you like (a–z, 0–9, _ and -); names ending in _asc rank lowest first, for speedruns. Link your app to one of your Arcade games in the console and its default board shows on that game's page. Anywhere else, use [starwide_leaderboard app="ID" board="default" limit="10"].

POST/apps/me/scoresBearer

{"score": 1200, "board": "default", "meta": "level 3"} → personal_best, best, rank. Scope: arcade.write.

POST/apps/me/achievementsBearer

{"key": "first_win", "title": "First win"} → newly_unlocked. GET lists the member's achievements. Scope: arcade.write.

GET/apps/leaderboard?board=default&period=all|week|day&limit=10Bearer

Top scores. App credentials only, no member needed.

Store & licence keys

Starwide staff can link a WooCommerce product to your app and switch on licence keys. Buyers get one key per unit (SW-XXXXX-XXXXX-XXXXX-XXXXX) when their order completes; refunds and cancellations revoke them. Store sales are paid to Starwide.

POST/apps/licenses/verifyBearer

{"license_key": "SW-…", "activate": true} → valid, status, product, member, activations. Needs X-Starwide-Secret, so call it from your server. Only keys for products linked to your app are found.

GET/apps/me/owns?product_id=123Bearer

Whether the member bought a store product, plus their keys if it's linked to your app. Scope: purchases.read.

GET/apps/me/subscriptionsBearer

Active creator tiers (like Starwide Tour) and WooCommerce subscriptions. Scope: purchases.read.

In-app purchases

Sell your own items, paid from the member's Starwide wallet straight into yours (Starwide keeps 0%). Members always confirm on Starwide; your app never sees how they pay. Up to $500.00 per purchase.

  1. Ask for the payment (scope payments): POST /apps/me/charges with amount, description, optional item_id and return_url (same site as your Callback URL).
  2. Send the member to the approve_url in the response.
  3. They come back to return_url?charge_id=…&status=paid (or declined). Don't trust the query string: confirm with GET /apps/charges/{id} or the charge.paid webhook, then deliver the item.
javascript
const charge = await fetch('https://www.starwide.net/wp-json/starwide/v1/apps/me/charges', {
  method: 'POST',
  headers: { Authorization: `Bearer ${accessToken}`, 'X-Starwide-App': TITLE_TOKEN, 'Content-Type': 'application/json' },
  body: JSON.stringify({ amount: 4.99, description: 'Golden skin', item_id: 'skin_gold', return_url: 'https://yourapp.example/shop/done' }),
}).then(r => r.json());
location.href = charge.approve_url; // pending requests expire after 30 minutes

Webhooks

Set a Webhook URL in the console and Starwide will POST JSON events to it: charge.paid, charge.declined, order.completed, license.issued, license.revoked, connection.revoked and ping (from Send test). Failed deliveries retry after 1 and 10 minutes; the console shows the latest results.

Check every request: X-Starwide-Signature is t=<time>,v1=<HMAC-SHA256 of "<time>.<raw body>" using your Secret Key>. Reject old timestamps.

javascript
// Node.js / Express — use the raw body, not parsed JSON
import crypto from 'node:crypto';
function verify(rawBody, header, secret) {
  const { t, v1 } = Object.fromEntries(header.split(',').map(p => p.split('=')));
  if (Math.abs(Date.now() / 1000 - Number(t)) > 300) return false;
  const expected = crypto.createHmac('sha256', secret).update(`${t}.${rawBody}`).digest('hex');
  return crypto.timingSafeEqual(Buffer.from(v1), Buffer.from(expected));
}

Errors & limits

Errors come back as JSON with a code, a message and an HTTP status.

json
{ "code": "swdev_auth_invalid", "message": "…", "data": { "status": 401 } }
401 swdev_auth_missingNo bearer token or Title Token sent.
401 swdev_auth_invalidToken revoked, or it isn't attached to this app.
403 swdev_app_suspendedThe app was suspended by Starwide.
429 swdev_rate_limitedMore than 1,000 calls this hour on one bearer token. Stack another token to spread load.

Bearer tokens

  • Your first bearer token is free. Each one after that costs $5.00 from your TeraWallet.
  • Tokens stack: an app can hold several, and any of them works. Revoke a leaked one instantly; the others keep working.
  • You can gift a token to any member, or sell it on the marketplace for $20.00 or more. The money goes to your wallet; Starwide takes no cut.
  • A token gets a brand-new value whenever it changes owner, so the old owner's copy stops working.
  • Members with tokens listed get a "Token(s) for Sale" badge on their profile.

Publishing

Use the Submit button at the bottom-right of the app console. Publish as Complete, or as Early Access while you're still building. Before either works:

  1. The app has its Title Token, Secret Key and at least one bearer token.
  2. You've picked a content rating: E, E10+, T, M or AO / NSFW.
  3. Your account is at least 30 days old.
  4. You're 18 or older (from the date of birth on your account).

Published apps appear in the Apps tab on your profile.

Data & privacy

What apps can read, once a member approves them through Sign in with Starwide:

  • Profile, bio, bio links and associated Pages
  • Followers and following
  • Feed history and uploaded public media
  • Their bearer token inventory
  • Wallet balance — the number only
  • Purchase history, with every NSFW purchase removed
  • Username and e-mail address (included with every connection)

Apps never get card numbers, bank details, e-wallet accounts or any saved payment method. Apps can link members to the Starwide wallet top-up page, but can't take payments themselves.

Player pins

Pin a timestamped comment on a video, song or post for a member (scope comments.write). It appears as an avatar pin on the Encore player's timeline, labelled "via your app", and follows the site's normal comment moderation: if response.pending is true, it shows once approved.

POST/apps/me/pinsBearer

{"object": "post:12" or "activity:34", "t": 83.5, "text": "The drop!", "public": true} → id, pending. Scope: comments.write.

GET/apps/pins?object=post:12Bearer

Public, approved pins on that item in time order. App credentials only.

Receiving members' posts

Build a fediverse bridge, cross-poster, bot or magazine: ask for scope feed.syndicate and set a Webhook URL. Members who connect your app get "Connected apps" in the "Also post to" menu when they write a post; when they pick it, you receive an activity.syndicated webhook with the text, link, image and author. Only public posts are sent, and NSFW posts only reach AO-rated apps for adult members.

json
{
  "event": "activity.syndicated",
  "data": {
    "activity_id": 812,
    "member": { "id": 3, "username": "buyer", "display_name": "Buyer Bee", "profile_url": "…" },
    "text": "Just hit 1,200 in Neon Drift!",
    "url": "https://starwide.net/activity/p/812/",
    "image": "",
    "nsfw": false,
    "idempotency_key": "swea-812-starwide_apps"
  }
}

Verified developers

Publish an app (Complete or Early Access) and you're a verified developer automatically: your profile picture becomes an octagon with a green check in the top-left corner, and visitors get an octagonal Commission button next to Follow. It opens your Encore commission settings (turn commissions on in your Studio) and any bearer tokens you have for sale. Unpublish every app and it goes back to normal. Brand accounts show square frames; everyone else is round.