Access keys and the API
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:
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
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:
{
"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
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).
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.
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"]}'Personal links
A member with no OpenMarket account yet gets a personal link instead. Ask for one per member, keyed by your own id for them:
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 write | Means |
|---|---|
maya | an 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": "..."}}:
| Status | Code | What it means |
|---|---|---|
| 404 | user_not_found | No one on OpenMarket has that username. |
| 422 | not_invite_only | The indicator is not Invite only. |
| 403 | fair_use | The Protected indicator is at 1,000 people (Fair use). |
| 429 | rate_limited | More 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>, withuntilset 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.