API
Workspaces API

On this page
- GET /workspaces/new
- POST /workspaces
- GET /workspaces/{workspace}
- GET /workspaces/{workspace}/settings
- POST /workspaces/{workspace}/ai-keys
- PATCH /workspaces/{workspace}/ai-keys/{aiKey}
- POST /workspaces/{workspace}/app-keys
- PATCH /workspaces/{workspace}/app-keys/{appKey}
- DELETE /workspaces/{workspace}/app-keys/{appKey}
- DELETE /workspaces/{workspace}/ai-keys/{aiKey}
- DELETE /workspaces/{workspace}
- POST /workspaces/{workspace}/members
- PATCH /workspaces/{workspace}/members/{member}
- DELETE /workspaces/{workspace}/members/{member}
- DELETE /workspaces/{workspace}/invitations/{invitation}
- POST /workspaces/{workspace}/ownership
- GET /invitations/{invitation}/{token}
- POST /invitations/{invitation}/accept
- POST /workspaces/{workspace}/api-keys
- POST /workspaces/{workspace}/api-keys/bulk
- DELETE /workspaces/{workspace}/api-keys/{apiKey}
- GET /api/v1/public
{workspace} is the workspace id. Two workspaces can share a name. An old /w/{slug} address redirects to the id. A person who is not a member receives 404. A member who cannot do the action receives 403.
GET /workspaces/new
- Auth: signed-in session.
- Name:
workspace.create - Request: none.
- Response: the workspace name page when the user has no workspace. Otherwise redirect to app home.
- Errors: guests are redirected to login.
POST /workspaces
- Auth: signed-in session with no workspace.
- Name:
workspace.store - Request:
name(required, max 100),icon(optional image, png, jpg, jpeg, or webp, max 2048 KB). - Response: redirect to How to create an app? in the new workspace. The workspace starts on Free with an empty usage period.
- Errors: validation errors redirect back. A user who already has a workspace is redirected to app home.
GET /workspaces/{workspace}
- Auth: signed-in member.
- Name:
workspace.home - Request: optional
notifiable_last_appcookie. - Response: redirect to the last app in this workspace, otherwise its oldest app, otherwise app creation.
- Errors: 404 when the user is not a member.
GET /workspaces/{workspace}/settings
- Auth: signed-in member.
- Name:
workspace.settings - Request: none.
- Response: the workspace settings page: people, connections, usage from the open period, the plan catalog, and API keys. Connections includes the Apple team and key identifier when one is saved, and never the signing key.
aiKeysandappKeyslist each saved key asid,provider, andname. A developer, admin, or owner also receivesapi_keywith the first 8 characters visible and the rest replaced by asterisks. A viewer does not.appKeysproviders arerevenuecat,stripe,discord,slack,resend, andhttp.serverKeyslists workspace server API keys as{ id, name, prefix, expires, lastUsed, usage, limit }. The prefix is not the full secret.createdKeyis the fullntf_key_secret only on the visit immediately after create, otherwise null. Billing actions are for the owner. - Errors: 404 when the user is not a member.
POST /workspaces/{workspace}/ai-keys
- Auth: signed-in developer, admin, or owner.
- Name:
workspace.ai-keys.store - Request:
provider(openai,openrouter,anthropic, orgemini),name(required, max 80, unique for that provider on this workspace), andapi_key(required, max 500). - Response: redirect back. The key is stored for this workspace and does not replace another key. Later pages list the name, and a developer, admin, or owner sees the first 8 characters of the value.
- Errors: 403 for a viewer. 404 when the user is not a member. 422 when the provider, name, or key is missing, or the name is already used for that provider.
PATCH /workspaces/{workspace}/ai-keys/{aiKey}
- Auth: signed-in developer, admin, or owner.
- Name:
workspace.ai-keys.update - Request:
name(required, max 80, unique for that provider on this workspace, ignoring this key) andapi_key(optional, max 500). A blankapi_keykeeps the saved value. - Response: redirect back.
- Errors: 403 for a viewer. 404 when the user is not a member, or the key is not on this workspace. 422 when the name is missing or already used.
POST /workspaces/{workspace}/app-keys
- Auth: signed-in developer, admin, or owner.
- Name:
workspace.app-keys.store - Request:
provider(revenuecat,stripe,discord,slack,resend, orhttp),name(required, max 80, unique for that provider on this workspace), andapi_key(required, max 500). - Response: redirect back. The credential is stored for this workspace.
- Errors: 403 for a viewer. 404 when the user is not a member. 422 when the provider, name, or key is missing, or the name is already used for that provider.
PATCH /workspaces/{workspace}/app-keys/{appKey}
- Auth: signed-in developer, admin, or owner.
- Name:
workspace.app-keys.update - Request:
name(required, max 80, unique for that provider on this workspace, ignoring this key) andapi_key(optional, max 500). A blankapi_keykeeps the saved value. - Response: redirect back.
- Errors: 403 for a viewer. 404 when the user is not a member, or the key is not on this workspace. 422 when the name is missing or already used.
DELETE /workspaces/{workspace}/app-keys/{appKey}
- Auth: signed-in developer, admin, or owner.
- Name:
workspace.app-keys.destroy - Request: none.
- Response: redirect back. The credential is deleted.
- Errors: 403 for a viewer. 404 when the user is not a member, or the key is not on this workspace.
DELETE /workspaces/{workspace}/ai-keys/{aiKey}
- Auth: signed-in developer, admin, or owner.
- Name:
workspace.ai-keys.destroy - Request: none.
- Response: redirect back. The key is deleted.
- Errors: 403 for a viewer. 404 when the user is not a member, or the key is not on this workspace.
DELETE /workspaces/{workspace}
- Auth: signed-in owner.
- Name:
workspace.destroy - Request: none.
- Response: redirect to app home. The workspace is deleted.
- Errors: 403 for other members. 404 when the user is not a member.
POST /workspaces/{workspace}/members
- Auth: signed-in admin or owner.
- Name:
workspace.members.store - Request:
email(required email),role(viewer,developer, oradmin). - Response: redirect back. Sends an invitation email. Replaces a pending invite for the same workspace and email.
- Errors: 403 for a viewer or developer. Validation when the email already belongs, the role is owner, or the seat cap is full.
PATCH /workspaces/{workspace}/members/{member}
- Auth: signed-in admin or owner.
- Name:
workspace.members.update - Request:
role(viewer,developer, oradmin). - Response: redirect back.
- Errors: 403 for a viewer or developer. 404 when the member is not in that workspace.
DELETE /workspaces/{workspace}/members/{member}
- Auth: signed-in member when removing themself. Admin or owner when removing someone else.
- Name:
workspace.members.destroy - Request: none.
- Response: redirect to app home when leaving, otherwise back.
- Errors: 403 when a viewer or developer removes someone else. The owner cannot leave.
DELETE /workspaces/{workspace}/invitations/{invitation}
- Auth: signed-in admin or owner.
- Name:
workspace.invitations.destroy - Request: none.
- Response: redirect back. The pending invite is cancelled.
- Errors: 403 for a viewer or developer. 404 when the invitation is not for that workspace.
POST /workspaces/{workspace}/ownership
- Auth: signed-in owner.
- Name:
workspace.ownership.transfer - Request:
member(required UUID of an existing member). - Response: redirect back. That member becomes owner. The previous owner becomes an admin.
- Errors: 403 for other members. 404 when the member is not in that workspace.
GET /invitations/{invitation}/{token}
- Auth: signed URL. Guest or signed-in session.
- Name:
workspace.invitations.accept - Request: the token in the path, plus the signature.
- Response: when the signed-in email matches, accept and redirect to the workspace. When the visitor is a guest, store the invite and redirect to login. An unknown email continues through registration first.
- Errors: 403 when the signature is invalid. Redirect home when the signed-in email does not match or the invite has expired.
POST /invitations/{invitation}/accept
- Auth: signed-in session. The account email must match the invite.
- Name:
workspace.invitations.accept.account - Request: none.
- Response: accept and redirect to the workspace.
- Errors: redirect to app home when the invite is expired, already accepted, or sent to a different email.
POST /workspaces/{workspace}/api-keys
- Auth: signed-in developer, admin, or owner.
- Name:
workspace.api-keys.store - Request:
name(required, max 80),expiration(never,30,90, or365),request_limit(unlimited,1000, or10000),limit_period(none,daily, ormonthly). The key belongs to the workspace.nonewith a numeric limit does not reset.unlimitedignores the period. - Response: redirect back. The next settings page shows the full
ntf_key_secret once increatedKey. Later visits do not. - Errors: guests are redirected to login. 403 for a viewer. 404 when the user is not a member. Validation error when a field is missing or not one of the allowed values.
POST /workspaces/{workspace}/api-keys/bulk
- Auth: signed-in developer, admin, or owner.
- Name:
workspace.api-keys.bulk - Request:
actionmust berevoke.idsis 1 to 500 API key ids. - Response: redirect back. Only keys in this workspace are revoked.
- Errors: 403 for a viewer. 404 when the user is not a member. 422 when the action or ids are invalid.
DELETE /workspaces/{workspace}/api-keys/{apiKey}
- Auth: signed-in developer, admin, or owner.
- Name:
workspace.api-keys.destroy - Request: API key id in the path.
- Response: redirect back. The key is deleted.
- Errors: guests are redirected to login. 403 for a viewer. 404 when the user is not a member or the key belongs to another workspace.
GET /api/v1/public
- Auth:
Authorization: Bearerwith a workspace server API key. - Name:
api.v1.public.show - Request: none.
- Response:
200WORKSPACE_FETCHED.datahasid,name,plan, andlimits. - Errors:
401INVALID_API_KEY.429API_KEY_LIMIT_REACHEDorRATE_LIMIT_EXCEEDED.500SERVER_ERROR.
