Access keys and the API

View as MarkdownOpen the editor

Let your own site keep the people list: an access key lets your server add and remove people on one Invite only indicator as they pay and cancel.

Make a key

In the People panel's corner menu, Access key opens the key sheet. Give the key a label (where it lives: your site, a script) and press Create key.

  • The key belongs to your account and to this one indicator. It can add, remove and list people there, and nothing else.
  • It is shown once. Copy it into your server's secret store.
  • Revoke stops it at once: calls with it fail from then on.
  • Make as many as you need, one per system, so each can be revoked on its own.

Keep the key on your server. Never put it in a web page, an app or a public repository.

The calls

Every call goes to your indicator's address on the registry and carries the key as a bearer token:

text
Base URL       https://registry.openmarket.xyz/v1/packages/@you/your-indicator
Authorization  Bearer <access key>

PUT     /access/<username>     add someone, or change their end date
DELETE  /access/<username>     remove someone
GET     /access/<username>     check one person
GET     /access                everyone with access, a page at a time
POST    /access/batch          up to 500 adds and removes in one call
POST    /access/links/batch    a personal link for each of your members
GET     /access/links.csv      the personal links nobody has opened yet

@you/your-indicator is your indicator's full name: your username, then the name you published it under. The key sheet shows the address under Calls go to.

Add someone

text
curl -X PUT "https://registry.openmarket.xyz/v1/packages/@you/your-indicator/access/maya" \
  -H "Authorization: Bearer $ACCESS_KEY" \
  -H "Content-Type: application/json" \
  -d '{"until": "2027-01-01T00:00:00Z"}'

The body is optional: leave it out and access has no end date. A second PUT replaces the end date, so a renewal sends the new one. The answer is 200 with the person's row:

json
{
  "username": "maya",
  "account_id": "<account id>",
  "until": "2027-01-01T00:00:00Z",
  "since": "2026-10-05T09:12:33Z",
  "via": "api"
}

Keep the account_id. It still finds the person after they rename their account.

Remove someone

text
curl -X DELETE "https://registry.openmarket.xyz/v1/packages/@you/your-indicator/access/maya" \
  -H "Authorization: Bearer $ACCESS_KEY"

The answer is 204 whether or not they were on the list, so a cancellation that arrives twice does no harm.

Check and list

GET /access/<username> answers 200 with the person's row, or 404 when they are not on the list.

GET /access reads everyone, 50 rows a page by default and up to 200 with limit. Pass a page's next_cursor back as cursor to read the next one. q filters by name, and view picks all, ending (access ending soon) or waiting (personal links nobody has opened yet).

text
curl "https://registry.openmarket.xyz/v1/packages/@you/your-indicator/access?limit=200&view=all" \
  -H "Authorization: Bearer $ACCESS_KEY"

Many at once

POST /access/batch takes up to 500 adds and removes in one call: the first import of your member list, or a nightly check that the list still matches yours. Each add row works like a PUT (an until of null means no end date), and each remove like a DELETE.

text
curl -X POST "https://registry.openmarket.xyz/v1/packages/@you/your-indicator/access/batch" \
  -H "Authorization: Bearer $ACCESS_KEY" \
  -H "Content-Type: application/json" \
  -d '{"add":[{"subject":"maya","until":null}],"remove":["sam"]}'

A member with no OpenMarket account yet gets a personal link instead. Ask for one per member, keyed by your own id for them:

text
curl -X POST "https://registry.openmarket.xyz/v1/packages/@you/your-indicator/access/links/batch" \
  -H "Authorization: Bearer $ACCESS_KEY" \
  -H "Content-Type: application/json" \
  -d '{"links":[{"ref":"member-1042","until":null}]}'

Send each member their link yourself. When they open it, they sign in or sign up and get access. GET /access/links.csv downloads the links nobody has opened yet.

Naming a person

Wherever a call takes a username, it also takes two other forms. In a batch they go in subject.

You writeMeans
mayaan OpenMarket username
id:<account id>the account_id a call returned; it still works after a rename
ref:<your reference>your own id for a member you sent a personal link

Errors

An error comes back as {"error": {"code": "...", "message": "..."}}:

StatusCodeWhat it means
404user_not_foundNo one on OpenMarket has that username.
422not_invite_onlyThe indicator is not Invite only.
403fair_useThe Protected indicator is at 1,000 people (Fair use).
429rate_limitedMore than 600 writes in a minute on this key. Wait the seconds the retry-after header names.

Wire it to your billing

Two calls cover a membership, keyed by the OpenMarket username you collect at checkout:

  • On signup, PUT /access/<username>, with until set to the date they have paid through. Send it again on each renewal, and access lapses on its own if a renewal never comes.
  • On cancellation or refund, DELETE /access/<username>.

No username at checkout? Make each customer a personal link instead, and send it with your welcome email.

Selling through Whop, or behind a paid Discord role? Connect Discord or Whop has a small script for each.

Selling with Stripe, on Patreon or through a paid Telegram group? Ready-made recipes has a complete file for each, ready to run.

Webhooks get missed now and then. A nightly batch that compares the list with your own member table catches whatever slipped through.