creating OAuth applications

Creating applications that read and post on behalf of vessels on nnyyxx is easy. Using curl as an example, see how easy it is to get started.

what we're doing here

OAuth is how your app asks a user "can I do things on your behalf?" without ever touching their password. They click Allow on a nnyyxx page, your app gets a token, and from then on you can call the API as them, scoped to one specific vessel they choose with the permissions they grant (read, write, or both).

OAuth grants cover one vessel. Account settings, personal filters, and activity attributed to another vessel are unavailable to apps. Public content remains readable subject to its access rules. Reauthorizing replaces the approved scopes and invalidates the previous tokens.

Here's the journey at a glance: register your app, generate a PKCE pair, send the user to the consent page, trade the code they come back with for tokens, call the API, then refresh or revoke when the time comes. Six small steps, and we'll walk through the why for each.

Pick an app from the sidebar (or register one if you haven't yet) and the snippets below fill in your client_id, redirect_uri, and a fresh PKCE pair generated right in your browser.

1. register your app

Head to /developer/apps/new and tell nnyyxx about your app: a name (users see this on the consent screen), one or more redirect URIs (where nnyyxx sends people back after they click Allow), and the permissions you might need (this is the ceiling, you can ask for less at consent time but never more). Your client_secret shows up once, so grab it the moment it appears. Save it as CLIENT_SECRET=… in your shell, and the curl commands below will pick it up.

2. create a PKCE pair

PKCE (pronounced "pixie") is the secret-handshake half of this flow. Your app makes up a long random secret called the verifier, then sends only its SHA-256 fingerprint, the challenge, when starting the flow. Later, when you redeem the code for a token, you prove you're the same app by handing over the original. If anyone snags the code in transit, it's useless without that verifier. PKCE is required for every nnyyxx OAuth flow.

Good news: your browser already generated a fresh pair for this session. Copy the verifier into your shell so it's ready when you need it:

Challenge: …

in a real app, you don't do this by hand

Most OAuth client libraries handle PKCE for you out of the box. Node: openid-client, simple-oauth2. PHP: league/oauth2-client. Go: golang.org/x/oauth2 (GenerateVerifier). Python: authlib. Ruby: oauth2 gem.

Prefer a GUI? Postman, Insomnia, and Bruno all speak PKCE natively. Pick OAuth 2.0 → Authorization Code (with PKCE), set Auth URL to https://nyx.now/oauth/authorize and Token URL to https://nyx.now/oauth/token, paste your credentials from /developer/apps, choose Code Challenge Method SHA-256, click Get New Access Token, and you're off. (Skip RapidAPI here, that's for discovering APIs, not testing OAuth flows.)

3. send the user to consent

This is the moment your real users meet your app. They land on a nnyyxx consent page, pick which of their vessels to grant access to, click Allow, and bounce back to your redirect URI with ?code=…&state=test. Save the code as CODE=…, you'll trade it for a token in the next step.

About state: we set it to test here so you can watch it round-trip. In production, generate a random value per request and verify it matches when the user comes back. It's your CSRF guard, and free to add.

Errors render in-app (no redirect): malformed scope, mismatched redirect_uri, or scope outside what the app declared.

4. exchange the code for tokens

Now the code-for-token swap. Why a separate code-then-token step in the first place? The code traveled through the user's browser, where anyone watching the network could have peeked. The actual token never does, your server fetches it directly. The PKCE verifier proves you're the same app that started the flow.

POST the code, your code_verifier, and your client credentials. The response gives you an access_token (good for an hour), a refresh_token (good for a year), and confirmation of the scope the user granted.

Save: ACCESS_TOKEN and REFRESH_TOKEN from the response.

5. call the API on the user's behalf

You made it. Drop the access token into the Authorization header on any endpoint in the API docs and you're calling nnyyxx as the user, on the one vessel they granted. Try pointing the same token at a different vessel and you'll get a 403 forbidden, that's the scoping working as intended. Anything that creates, edits, or deletes also needs the write scope on top of the vessel grant, and the staff tools (pinning, attendee exports, ticket check-in) need manage. An app can call the endpoints in the API docs and nothing else: any other route answers 403 endpoint_not_available. A token that has expired or been revoked answers 401 invalid_token on every endpoint, public reads included: refresh and retry.

6. refresh and revoke

Access tokens last an hour. Before yours expires (check expires_in on the token response), trade your refresh token for a fresh pair. Refresh tokens rotate: every swap retires the old one. If something ever tries to replay a refresh from two generations back, we treat it as a leak and burn the whole grant. Better safe than sorry.

One authorization holds ONE live token pair. A refresh, or the same user authorizing your app on the same vessel again (from a second device, say), replaces the previous pair, so share one token store across your servers and devices rather than refreshing from each. Every token call needs your client_secret: keep it on a server you control, never inside a mobile or browser app. Token calls are budgeted per app (3000 a minute by default, no per-address limit); a wrong client secret pays a per-address guard instead.

When you're done with a token, or the user uninstalls your app, hit the revoke endpoint. It works on either an access or a refresh token and ends the whole authorization (both tokens), it's idempotent (calling it twice is fine), and the user can revoke from their side anytime at /settings/apps.