Introduction
"Sign in with 4S" is an OAuth 2.0 / OpenID Connect (OIDC) compliant authentication feature. External applications can let users sign in with their 4S account and access the 4S read (GET) APIs on their behalf.
Because the implementation is standards-compliant, common OIDC libraries such as NextAuth (Auth.js) and openid-client work out of the box. The only supported flow is Authorization Code with PKCE (S256).
Register your app from the Developer menu on 4S (https://4s.link/developer). You will receive a client_id and a client_secret. The client_secret is shown only once at registration — store it securely.
Basics
| Item | Value |
|---|---|
| Issuer | https://api.4s.link |
| Supported flow | Authorization Code + PKCE (S256) |
| Client authentication | client_secret_basic / client_secret_post / none (public + PKCE) |
| ID Token signature | RS256 |
| Access token lifetime | 1 hour |
| Refresh token lifetime | 60 days (rotating) |
# OIDC Discoveryhttps://api.4s.link/.well-known/openid-configuration# JWKS (公開鍵)https://api.4s.link/.well-known/jwks.json# 認可エンドポイントhttps://4s.link/oauth/authorize# トークンエンドポイントhttps://api.4s.link/oauth/token# UserInfohttps://api.4s.link/oauth/userinfo# トークン失効https://api.4s.link/oauth/revoke
Authentication Flow
The Authorization Code + PKCE flow works as follows: (1) your app generates a code_verifier and redirects the user to the authorization endpoint with its S256 hash (code_challenge); (2) the user signs in to 4S and grants consent, and an authorization code is returned to your redirect_uri; (3) your app exchanges the code together with the code_verifier for tokens at the token endpoint.
https://4s.link/oauth/authorizeThe authorization endpoint is a URL you redirect the user's browser to (it is not an API). Attach the following query parameters.
Query parameters
response_typestringrequiredMust be code
client_idstringrequiredClient ID issued in the Developer menu
redirect_uristringrequiredMust exactly match a registered redirect URI
statestringoptionalRandom value for CSRF protection, returned unchanged in the callback (strongly recommended)
noncestringoptionalRandom value returned as the nonce claim in the ID Token (recommended)
code_challengestringrequiredbase64url-encoded SHA-256 hash of the code_verifier
code_challenge_methodstringrequiredMust be S256
promptstringoptionalSet to consent, select_account or login to show the consent (account selection) screen. Apps other than official 4S apps always show the consent screen, even if already granted. none is not supported and returns interaction_required
After the user approves, the browser returns to your redirect_uri with ?code=...&state=.... If the user denies or validation fails, ?error=access_denied (or similar) is returned instead. Authorization codes are single-use and expire after 5 minutes.
const crypto = require("crypto");const codeVerifier = crypto.randomBytes(32).toString("base64url");const codeChallenge = crypto.createHash("sha256").update(codeVerifier).digest("base64url");console.log({ codeVerifier, codeChallenge });
Discovery / JWKS
/.well-known/openid-configurationThe OIDC Discovery document. It returns metadata such as endpoint URLs and signing algorithms. Configure your OIDC library with this origin as the issuer and everything else resolves automatically.
/.well-known/jwks.jsonThe public keys (JWK Set) for verifying ID Token signatures. Verify the RS256 signature using the key whose kid matches the ID Token header. Keys may be rotated, so fetch the JWKS as needed or implement cache expiry (Cache-Control: max-age=300).
curl https://api.4s.link/.well-known/openid-configuration
Token Endpoint
/oauth/tokenExchanges an authorization code for tokens, or refreshes tokens with a refresh token. Send the request body as application/x-www-form-urlencoded. Confidential clients authenticate with client_secret_basic (Authorization: Basic) or client_secret_post (in the body).
Parameters for grant_type=authorization_code
grant_typestringrequiredauthorization_code
codestringrequiredThe authorization code received in the callback
redirect_uristringrequiredSame value as in the authorization request
code_verifierstringrequiredThe PKCE verifier the code_challenge was derived from
Parameters for grant_type=refresh_token
grant_typestringrequiredrefresh_token
refresh_tokenstringrequiredA valid refresh token
Refresh tokens rotate: every refresh_token grant issues a new refresh token and invalidates the old one. If a revoked refresh token is reused, all tokens for that user-client pair are revoked as a safety measure. Always persist the latest refresh token.
curl -X POST https://api.4s.link/oauth/token \-u "YOUR_CLIENT_ID:YOUR_CLIENT_SECRET" \-H "Content-Type: application/x-www-form-urlencoded" \-d "grant_type=authorization_code" \-d "code=AUTHORIZATION_CODE" \-d "redirect_uri=https://example.com/callback" \-d "code_verifier=CODE_VERIFIER"
ID Token
The token response always contains an ID Token (an RS256-signed JWT). To validate it, verify (1) the signature with the JWKS key matching the kid, (2) iss equals the issuer, (3) aud equals your client_id, (4) exp is in the future, and (5) the nonce claim matches if you sent one. OIDC libraries handle all of this automatically.
Main claims
| Claim | Description |
|---|---|
sub | Unique user ID (the 4S user ID) |
iss | Issuer |
aud | Your client ID |
auth_time | When the user authorized (Unix seconds) |
nonce | The nonce from the authorization request (if sent) |
name / given_name / family_name / picture / profile / preferred_username / locale | Profile info (always included) |
email / email_verified | Email address (always included) |
{"sub": "a1b2c3d4-....","iss": "https://api.4s.link","aud": "YOUR_CLIENT_ID","iat": 1783900000,"exp": 1783903600,"auth_time": 1783899990,"nonce": "RANDOM_NONCE","name": "太郎 山田","given_name": "太郎","family_name": "山田","picture": "https://.../avatar.png","profile": "https://4s.link/taro","preferred_username": "taro","locale": "ja","email": "taro@example.com","email_verified": true}
Token Revocation
/oauth/revokeRFC 7009 compliant token revocation endpoint. Revoke tokens that are no longer needed, e.g. when the user signs out of your app. Revoking a refresh token also revokes the access tokens issued from it. Per RFC 7009, unknown tokens still return 200.
Body parameters
tokenstringrequiredThe access token or refresh token to revoke
curl -X POST https://api.4s.link/oauth/revoke \-u "YOUR_CLIENT_ID:YOUR_CLIENT_SECRET" \-H "Content-Type: application/x-www-form-urlencoded" \-d "token=4s_rt_..."
Calling the APIs
Call the 4S APIs by sending your access token in the Authorization: Bearer header. OAuth access tokens can only call the read and write endpoints listed in the categories below (the allowlist); any other method + path combination returns 403.
Conventions
| Item | Detail |
|---|---|
| Base URL | https://api.4s.link |
| Auth | Authorization: Bearer <access_token> |
| Allowed scope | Only the method + path combinations listed in each category (GET / POST / PUT / PATCH / DELETE) |
| Pagination | List endpoints take page / perPage query params; responses include pageInfo (totalCount / totalPages / page / perPage) |
| Rate limits | 60 req/min and 1,000 req/hour per app × user by default. Every response carries X-RateLimit-* headers (see "Rate Limits") |
| Error (401) | Token invalid, revoked or expired |
| Error (403) | A method + path outside the allowlist (everything under /admin is always denied) |
| Permissions | Per-endpoint permission checks are identical to a normal signed-in session: operations requiring an event staff role or organization/community admin rights still require the authorizing user to hold them |
| Email addresses | `email` fields are recursively removed from responses under /manage (free-text survey answers are not covered) |
Every available endpoint is listed in the categories below. Trailing slashes are ignored and the /api and /v1 prefixes resolve to the same endpoints. Paths are case-sensitive, and the HEAD method is not supported.
curl https://api.4s.link/users/me \-H "Authorization: Bearer 4s_at_..."
Rate Limits
API calls made with an access token are rate limited per app and user pair (app × user). Because the counter is per user, one user hitting the limit does not affect calls made on behalf of your other users. Separately, each app has an overall limit (600 requests per minute and 20,000 per hour).
Limits
| Window | Default limit | Description |
|---|---|---|
| 1 minute | 60 | Caps short bursts of traffic |
| 1 hour | 1,000 | Overall volume cap, which also absorbs bursts that straddle a minute boundary |
Limits can be raised or lowered per app. If the defaults do not fit your use case, contact the 4S team. You can check the limits currently applied to your app on its detail page in the developer menu, or from the response headers below.
Response headers
| Header | Description |
|---|---|
X-RateLimit-Limit-Minute | Limit for the 1 minute window |
X-RateLimit-Remaining-Minute | Remaining calls in the current 1 minute window |
X-RateLimit-Limit-Hour | Limit for the 1 hour window |
X-RateLimit-Remaining-Hour | Remaining calls in the current 1 hour window |
X-RateLimit-Reset | When the tighter of the two windows resets (Unix seconds) |
Retry-After | Returned on 429 only. Seconds to wait before retrying |
These headers are returned on every successful response, not just on 429. Pace your requests using the remaining counts and you can avoid hitting 429 at all. They are exposed via CORS, so browser-based apps can read them too.
Exceeding a limit returns 429 Too Many Requests with a body of { "error": "...", "errorCode": 60007 }. Requests made while over the limit still count toward it, so wait for the number of seconds in Retry-After before retrying. Retrying immediately in a loop only delays recovery.
Rate limits apply only to API calls made with an access token. Discovery, JWKS, the token endpoint (/oauth/token) and the revocation endpoint (/oauth/revoke) are not rate limited.
curl -i https://api.4s.link/users/me \-H "Authorization: Bearer 4s_at_..."# HTTP/1.1 200 OK# X-RateLimit-Limit-Minute: 60# X-RateLimit-Remaining-Minute: 43# X-RateLimit-Limit-Hour: 1000# X-RateLimit-Remaining-Hour: 912# X-RateLimit-Reset: 1755500460
{"error": "Too many requests. Please slow down.","errorCode": 60007}
Error Reference
The token endpoint, UserInfo and events APIs return RFC 6749 / RFC 6750 style errors ({ error, error_description }). Authorization endpoint errors are returned as query parameters on the redirect_uri (unless the redirect_uri itself is invalid).
| Error | Where | Description |
|---|---|---|
invalid_client | token / revoke | Client authentication failed, or the app is suspended |
invalid_grant | token | Invalid, expired or already-used code / refresh token, PKCE verification failure, redirect_uri mismatch, or consent revoked |
invalid_request | token / authorize | Missing or malformed required parameters |
access_denied | authorize | The user denied the authorization request |
interaction_required | authorize | prompt=none was requested but interaction is required (silent auth is not supported) |
unsupported_grant_type | token | A grant_type other than authorization_code / refresh_token |
401 Unauthorized | Resource APIs | The access token is invalid, revoked or expired, or the app is suspended |
403 Forbidden | Resource APIs | Access to an endpoint outside the allowed GET list (only the endpoints listed in each category are usable) |
429 Too Many Requests | Resource APIs | Rate limit exceeded (errorCode: 60007). Wait the number of seconds in Retry-After before retrying (see "Rate Limits") |
{"error": "invalid_grant","error_description": "PKCE verification failed"}
Integration Examples
With generic OIDC libraries, setting the issuer is enough — endpoints resolve automatically via Discovery. A NextAuth (Auth.js) v5 configuration example is shown on the right. Don't forget to register your redirect_uri (e.g. https://your-app.com/api/auth/callback/4s) in the Developer menu.
Known limitations: prompt=none (silent authentication) and Dynamic Client Registration are not supported. ID Tokens do not include the at_hash claim.
import NextAuth from "next-auth";export const { handlers, auth, signIn, signOut } = NextAuth({providers: [{id: "4s",name: "4S",type: "oidc",issuer: "https://api.4s.link",clientId: process.env.FOURS_CLIENT_ID,clientSecret: process.env.FOURS_CLIENT_SECRET,authorization: {params: { scope: "openid profile email" },},// PKCE と state は NextAuth がデフォルトで有効化します},],});
Demo App
A working "Sign in with 4S" sample built with Next.js + TypeScript. See how the login flow (Authorization Code + PKCE) and profile retrieval are implemented.
Users / Profile
- GET
/usersList / search usersSearch and list users. `search` is AND-matched by whitespace across slug, name, title and bio. - GET
/users/:idUser detailRetrieve a user's public profile (email not included). - GET
/users/meOwn profileRetrieve the authenticated user's own profile, including private fields such as email. - PUT
/users/meUpdate own profileUpdate the authenticated user's own profile. Only the fields you send are updated (partial update); sending null clears a value. - POST
/users/me/slugSet own slugSet the slug used in the profile URL. A slug can only be changed once every 2 weeks: changing it to a different value within 14 days of the previous change returns 400 (errorCode 10012) with nextChangeableAt (UTC) in the response. Sending the current value is exempt from the limit and returns 200 without updating. - POST
/users/me/avatarUpload avatar imageUpload an avatar image as multipart/form-data and get the stored URL. The URL is also applied to the user's avatarUrl. - POST
/users/me/card-photoUpload business card photoUpload a photo to use as the business card background as multipart/form-data (field name cardPhoto) and get the stored URL. The photo is auto-oriented, scaled down to 1600px on its longer side and converted to WebP. This call does not change the profile; pass the returned URL as cardPhoto.url to PUT /users/me to save it. - POST
/users/me/event-face-photoUpload event face photoUpload the face photo used for on-site identity checks. Required before registering for events with requiresFacePhoto enabled. - GET
/users/me/organizationsOwn managed organizationsList organizations where the authenticated user is an ADMIN. - GET
/users/me/organization_membersOwn organization membershipsList the authenticated user's organization memberships (work / education history) shown in the profile. - GET
/users/me/unread-countsAggregated unread countsReturn the authenticated user's unread chat, announcement, and notification counts in a single request. Intended for surfaces that render several badges at once, such as tab bars and app icons. - GET
/users/me/schedulesList my schedulesList the schedules registered by the authenticated user. Schedules are taken into account when computing meeting availability: slots overlapping a schedule cannot be booked. - POST
/users/me/schedulesCreate a scheduleRegister a schedule. When linked to an event, overlapping meeting slots for that event become unavailable. - PUT
/users/me/schedules/:scheduleIdUpdate a scheduleUpdate one of your own schedules. - DELETE
/users/me/schedules/:scheduleIdDelete a scheduleDelete one of your own schedules. - GET
/users/:id/sessionsSpeaking sessionsRetrieve the sessions where the user is a confirmed speaker. Only sessions of published events that are not drafts are returned, ordered by start time descending. - GET
/users/:id/eventsParticipated eventsRetrieve events the user participated in (confirmed / checked in), ordered by start date descending. Private events are excluded. - GET
/users/:id/communitiesCommunitiesRetrieve communities the user belongs to (approved, non-private). No pagination. - GET
/users/:id/organization_membersUser's organization membershipsList a user's publicly visible organization memberships. - POST
/organization_memberAdd organization membershipAdd a membership (work / education history) for the authenticated user. Unless you created the organization, the role is forced to MEMBER. - PUT
/organization_member/:idUpdate organization membershipUpdate one of your own memberships. Other users' memberships return 403. - DELETE
/organization_member/:idDelete organization membershipDelete one of your own memberships.
Card exchange / Meet log
- GET
/card-exchanges/:exchangeIdGet a card exchangeReturn the exchange as seen by a participant. The requester polls this while waiting, and moves on with result once status becomes ACCEPTED. When result.candidateEvents has at least one event, ask where you met and record eventId with PATCH /meet-logs/:meetLogId (a single candidate is already recorded in result.eventId, so show it selected and send eventId: null if it is cleared). - POST
/card-exchangesStart a card exchange (scan)Send the token from the other person's My QR code (`https://4s.link/{slug}?ref=code&token=...`) to start a business card exchange. The response includes a summary of their profile to show on the request screen. The token proves the QR code was scanned in person, so a slug alone cannot start an exchange. Each user has at most one exchange in progress; starting a new one removes the previous one. When you scan a community's card QR code (`https://4s.link/communities/{communityId}?ref=code&token=...`, where the token belongs to the admin showing the card), send the community ID from the path as targetCommunityId. To request as a community you administer, send asCommunityId. - GET
/card-exchanges/currentGet the current card exchangeReturn the authenticated user's card exchange in progress, or null. Poll this while showing the My QR code, and open the confirmation screen when role is TARGET and status is PENDING. - PUT
/card-exchanges/:exchangeId/requestRequest a card exchangeAsk the scanned person to exchange cards (SCANNED → PENDING). A push notification is sent to their official app, and the request waits 3 minutes for approval. Any other exchange the person had in progress is removed. - PUT
/card-exchanges/:exchangeId/acceptAccept a card exchangeThe person who was asked accepts and completes the exchange. A meet log is recorded for each of the two people. If exactly one ongoing event has both people registered, it is recorded automatically as where you met. When one side exchanges as a community, that side's log becomes the community's log, shared by all current admins of the community. - PUT
/card-exchanges/:exchangeId/declineDecline a card exchangeThe person who was asked declines. The exchange is deleted, so the requester's GET /card-exchanges/:exchangeId returns 404. Declining an exchange that no longer exists also succeeds. - PUT
/card-exchanges/:exchangeId/cancelCancel a card exchangeThe requester cancels the exchange, before or after requesting. Canceling an exchange that no longer exists also succeeds. - GET
/meet-logs/contactsList exchanged contactsList the people you exchanged cards with, most recent first. Each person appears once no matter how many times you exchanged. By default only person-to-person exchanges are returned. includeCommunities=true mixes in the cards you exchanged with communities, and asCommunityId switches to the list of people that community exchanged with (admins only). - GET
/meet-logs/contacts/:userIdGet meet logs with a personReturn your meet logs with the given person (newest first) and the email sharing status. For someone you have not exchanged with, contact is null and meetLogs is empty. Only person-to-person exchanges are covered; exchanges with a community that the person handled as an admin are not included (see GET /meet-logs/communities/:communityId). With asCommunityId, returns that community's logs with the person (admins only). - PATCH
/meet-logs/contacts/:userIdChange email sharing for a personChoose whether to share your email address with the given person. You can change it per person at any time. - GET
/meet-logs/communities/:communityIdGet your card exchange with a communityReturn the authenticated user's card exchange with the given community: your sharing setting, the newsletter subscription and the logs. When you have not exchanged, or the community was deleted, contact is null and meetLogs is empty (no 404). The result does not depend on the community's visibility or on whether you are a member. Exchanging cards does not make you a member, and being a member does not subscribe you. - PATCH
/meet-logs/communities/:communityIdChange sharing and newsletter subscription for a communityChoose whether to share your email address with a community you exchanged cards with, and whether to receive its newsletter. While you receive the newsletter (subscribeNewsletter is true), your email address is disclosed to the community even if shareEmail is false. 4S does not send newsletters; the community's admins send them on their own, based on the subscription flag in the CSV. - PATCH
/meet-logs/:meetLogIdUpdate a meet logUpdate the memo or the event on your own meet log. The memo is visible only to you. The event must be one of the candidates (candidateEvents) from when you exchanged. - DELETE
/meet-logs/:meetLogIdDelete a meet logDelete a meet log. It is also removed from the other person's logs. Once all logs with a person are gone, they leave your contact list and email addresses are no longer visible. - POST
/meet-logs/exportEmail a CSV of contactsSend a download link for a CSV of your contacts (one row per person) to the authenticated user's email address. The link is valid for 24 hours. Your own CSV also contains the cards you exchanged with communities (told apart by the trailing Type column). With asCommunityId, the CSV lists the people that community exchanged with, including a Newsletter column.
Organizations
- GET
/organizationsList / search organizationsSearch and list organizations (career data). - GET
/organizations/:idOrganization detailRetrieve an organization's detail. - GET
/organizations/:id/membersList organization membersList the members of an organization. User objects contain public fields only and never include email.
Organizations (Admin)
- GET
/manage/organizations/:idOrganization detail (Admin)Retrieve an organization for management. Only its admins can access it. - POST
/manage/organizationsCreate organization (Admin)Create an organization. The creator is automatically registered as an ADMIN. No additional permission is required beyond being signed in. - PUT
/manage/organizations/:idUpdate organization (Admin)Update an organization. Only its admins can do this. - DELETE
/manage/organizations/:idDelete organization (Admin)Delete an organization. Admins only. - POST
/manage/organizations/:id/logoUpload organization logo (Admin)Upload the organization's logo image. - POST
/manage/organizations/:id/logo-darkUpload dark-theme logo (Admin)Upload the logo image used in dark theme. - GET
/manage/organizations/:id/membersList organization members (Admin)List members for management. Admins only. - POST
/manage/organizations/:id/membersAdd organization member (Admin)Add an existing user as an organization member. Admins only. - PUT
/manage/organizations/:orgId/members/:memberIdUpdate organization member (Admin)Update a member's title, period and role. Admins only. - DELETE
/manage/organizations/:orgId/members/:memberIdRemove organization member (Admin)Remove a member from the organization. Admins only. - PUT
/manage/organizations/:orgId/members/:memberId/roleChange member role (Admin)Change only a member's role. The last remaining ADMIN cannot be demoted.
Events
- GET
/eventsList / search eventsSearch and list events with rich filters. The public response omits startTime / endTime / onlineUrl (read schedules instead). groupByDate=true returns date-grouped results. - GET
/events/:idEvent detailRetrieve event detail. The path accepts an ID or a slug. Includes sessions, speakers, stages, tickets and organizers. - GET
/events/geojsonEvent map pinsReturns published events that have coordinates as a GeoJSON FeatureCollection. Not paginated; narrow the result with bbox. Filters have the same meaning as GET /events. - GET
/events/counts-by-dateDates that have eventsReturns only the dates within the given range that have events. Intended for marking days in a calendar; lighter than the list API. - GET
/events/my-entriesOwn participated eventsRetrieve events the authenticated user has registered for, ordered by entry date descending. - GET
/events/:eventId/is-staffCheck if you are event staffReturn whether the authenticated user is staff of the event. To obtain the role and its permission list, use GET /manage/events/:eventId/acl instead. - GET
/events/:eventId/chat-roomEvent chat roomGet the event's public chat room, creating it if it does not exist. Available to participants of events with chat enabled. - GET
/events/:eventId/participantsParticipantsRetrieve event participants (confirmed / checked in and accepting meetings). Access is controlled by the participant list visibility setting. - GET
/events/:eventId/participants/:userId/questionsA participant's survey answersReturn the survey questions and answers for a single event participant. Only public questions are included. Used when viewing a profile before requesting a meeting. - PATCH
/events/:eventId/entries/:entryId/meeting-preferenceMeeting request preferenceToggle whether the authenticated user accepts meeting requests for their own event entry. Setting it to false removes them from participant search and blocks new requests; existing meetings are unaffected. - GET
/events/:eventId/stagesList stagesList the stages (venues) of an event. - GET
/events/:eventId/sessionsList sessionsList the published sessions (timetable) of an event. - GET
/events/:eventId/sessions/:idSession detailRetrieve a single session. - GET
/events/:eventId/speakersList speakersList the published speakers of an event. - GET
/events/:eventId/speakers/:entryIdSpeaker detailRetrieve a single speaker, including their sessions. - GET
/events/:eventId/announcementsList event announcementsList event announcements. Participants only. - GET
/events/:eventId/announcements/:idEvent announcement detailRetrieve a single event announcement. Participants only. - GET
/events/:eventId/ticketsList ticketsList the ticket types on sale for an event. - GET
/events/:eventId/tickets/:ticketIdTicket detailRetrieve a single ticket type. - GET
/events/:eventId/questionsList survey questionsList the survey questions asked during event registration. - GET
/events/:eventId/my-questionsOwn survey answersReturn the survey questions and the authenticated user's own answers. - GET
/events/:eventId/meeting-spotsList meeting spotsList the meeting spots configured by the event organizers. - GET
/events/:eventId/referral-rankingReferral influence rankingRetrieve the influence ranking (top 100) based on the event's invitation chain. - GET
/events/:eventId/entriesOwn entry stateRetrieve the authenticated user's entries (non-canceled) for the specified event. - DELETE
/events/:eventId/entries/:entryIdCancel a registrationCancel your own registration. Already-canceled or checked-in registrations cannot be canceled. Paid tickets are refunded automatically according to the event's cancellation policy. - POST
/events/:eventId/entries/:entryId/invitation-codeCreate an invitation codeCreate a referral invitation code from the authenticated user's own entry. Each entry has one code; if it already exists, that code is returned (after syncing its target ticket and redemption limit with the ticket's current invitation settings). No request body is required. - GET
/events/:eventId/entries/:entryId/referral-ancestorsReferral ancestors (inviter chain)Walk the inviter chain upward from the given entry, returned nearest-first. Returns 403 unless the caller is a confirmed participant (CONFIRMED / CHECKED_IN) of the event. - GET
/events/:eventId/entries/:entryId/referral-descendantsReferral descendants (invite tree)Expand the invite tree downward from the given entry as root. Each node's direct children are paginated by childrenLimit + cursor and include a total child count (CONFIRMED / CHECKED_IN only). Returns 403 unless the caller is a confirmed participant. - POST
/events/:eventId/registerRegister for an eventRegister for an event. Free tickets are confirmed immediately as UNUSED (or PENDING for approval-based events). For paid tickets, complete payment first via the payment link or prepaid checkout endpoints. - POST
/events/:eventId/tickets/:ticketId/payment-linkCreate a payment linkCreate a Stripe payment link for a paid ticket. Send the user to the returned url; the registration is confirmed via webhook once payment completes. Use successUrl / cancelUrl to bring the user back to your own site after payment. - POST
/events/:eventId/tickets/:ticketId/prepaid-checkoutCreate prepaid checkoutCreate a Stripe Checkout session after storing the survey answers up front. Inventory is held for a limited time and released if payment is not completed. - POST
/events/:eventId/tickets/:ticketId/payment-intentCreate a payment sheet PaymentIntentCreate a PaymentIntent so a mobile app can charge through the Stripe payment sheet. Like prepaid checkout, survey answers and profile fields are stored first and inventory is held for a limited time. Entries are created by the payment-completed webhook. - PUT
/events/:eventId/entries/:entryId/transferTransfer a ticketTransfer an unused (UNUSED) ticket from a multi-ticket purchase to another user. Only the purchaser can transfer, and tickets marked non-transferable cannot be transferred. - POST
/questions/:questionId/answerAnswer a survey questionSubmit or update an answer to a survey question after registering. An existing answer is overwritten. - POST
/questions/:questionId/answer-imageUpload survey answer imageUpload an image for an image-type survey question and get the stored URL. Pass the URL as `answer` to POST /questions/:questionId/answer. - GET
/events/:eventId/meetingsList meetingsList the meetings the authenticated user is involved in, both sent and received. Meetings between other participants are not included. - POST
/events/:eventId/meetingsRequest a meetingRequest a meeting with another participant of the event. A direct chat room is created if one does not already exist, and the message body is posted as the first message. The meeting starts in the REQUESTED status with no spot or time set. - GET
/chat-rooms/:chatRoomId/meetingsMeetings of a chat roomReturn the meetings linked to a chat room. Used to render meeting controls or a meeting list inside a chat view. Returns 403 when the caller is not a member of the room. - PUT
/meetings/:meetingId/statusChange meeting statusHandles approve, reject, cancel, re-allow and undo-cancel in one endpoint. Whether a transition is permitted depends on the current status and the caller's role in the meeting. - PUT
/meetings/:meetingId/confirmSet meeting spot and timeSet the spot and time of an approved (CONFIRMED) meeting. Calling it again on a meeting that already has them changes the booking. - GET
/meetings/:meetingId/meeting-spotsSelectable meeting spotsList the meeting spots selectable for this meeting. Unlike the event-wide list (GET /events/:eventId/meeting-spots), the result is narrowed to the spots allowed for the participants' ticket types. - GET
/meetings/:meetingId/available-slotsAvailable time slotsReturn the time slots at which the meeting can be booked for the given spot and date. Slots are aligned to the event's meetingSlotDuration.
Events (Admin)
- GET
/manage/eventsList manageable events (Admin)List events where the authenticated user is registered as staff. - GET
/manage/events/:eventIdEvent detail (Admin)Retrieve an event for management. Requires staff with the eventRead permission. - POST
/manage/eventsCreate event (Admin)Create an event in DRAFT state. The creator is automatically registered as ADMIN staff. No additional permission is required beyond being signed in. Organizers (EventOrganizer) are specified via `organizers`. - PUT
/manage/events/:eventIdUpdate event (Admin)Update an event. Requires the eventWrite permission (meeting-only updates also accept eventMeetingWrite). Organizers (EventOrganizer) are reconciled from the `organizers` array: organizers not present in the array are removed. - DELETE
/manage/events/:eventIdDelete event (Admin)Delete an event. Requires the eventWrite permission. - GET
/manage/events/:eventId/aclOwn staff role and permissions (Admin)Return the authenticated user's staff role for the event along with the permission list for each role. Use it to drive management UI visibility. - PUT
/manage/events/:eventId/thumbnailUpload event thumbnail (Admin)Upload the event thumbnail image. Requires the eventWrite permission. - PUT
/manage/events/:eventId/main-imageUpload event main image (Admin)Upload the event page's main image. Requires the eventWrite permission. - POST
/manage/events/:eventId/description-imagesUpload description image (Admin)Upload an image to embed in the event description (Markdown) and get its URL. Requires the eventWrite permission. - GET
/manage/events/:eventId/staffList event staff (Admin)List the event's staff. Requires the staffRead permission. - POST
/manage/events/:eventId/staffAdd event staff (Admin)Add an existing user as event staff. Requires the staffInviteWrite or staffWrite permission. - PUT
/manage/events/:eventId/staff/:staffIdUpdate event staff role (Admin)Change a staff member's role. Requires the staffWrite permission. - DELETE
/manage/events/:eventId/staff/:staffIdRemove event staff (Admin)Remove an event staff member. Requires the staffWrite permission. - GET
/manage/events/:eventId/stagesList stages (Admin)List stages for management. Requires the stagesRead permission. - POST
/manage/events/:eventId/stagesCreate stage (Admin)Create a stage. Requires the stagesWrite permission. - PUT
/manage/events/:eventId/stages/:stageIdUpdate stage (Admin)Update a stage. Requires the stagesWrite permission. - DELETE
/manage/events/:eventId/stages/:stageIdDelete stage (Admin)Delete a stage. Requires the stagesWrite permission. - PUT
/manage/events/:eventId/stages/reorderReorder stages (Admin)Reorder stages in bulk. Requires the stagesWrite permission. - GET
/manage/events/:eventId/sessionsList sessions (Admin)List all sessions including drafts. Requires the sessionsRead permission. - GET
/manage/events/:eventId/sessions/:idSession detail (Admin)Retrieve a session for management. Requires the sessionsRead permission. - POST
/manage/events/:eventId/sessionsCreate session (Admin)Create a session. Requires the sessionsWrite permission. SessionSpeaker links are created from entryIds / speakers. - PUT
/manage/events/:eventId/sessions/:idUpdate session (Admin)Update a session. Requires the sessionsWrite permission. Sending entryIds reconciles the SessionSpeaker links to match the array. - DELETE
/manage/events/:eventId/sessions/:idDelete session (Admin)Delete a session; its SessionSpeaker links are removed as well. Requires the sessionsWrite permission. - PUT
/manage/events/:eventId/sessions/bulkBulk update sessions (Admin)Update multiple sessions at once, e.g. after dragging items in the timetable. Requires the sessionsWrite permission. - GET
/manage/events/:eventId/speakersList speakers (Admin)List all speakers including drafts. Requires the speakersRead permission. A speaker is a registration (EventEntry) with speaker metadata; its id is the registration ID. - GET
/manage/events/:eventId/speakers/:entryIdSpeaker detail (Admin)Retrieve a speaker for management, including confirmed sessions. Requires the speakersRead permission. - POST
/manage/events/:eventId/speakersAdd a 4S user as speaker (Admin)Register a 4S user as a speaker. Requires the speakersWrite permission. If the user already has an active registration, speaker metadata is attached to it; otherwise a registration is created as a draft (UNUSED). Guest users cannot be specified (create them with /speakers/guest). - PUT
/manage/events/:eventId/speakers/:entryIdUpdate speaker (Admin)Update speaker metadata (pickedUp and display overrides). For guest speakers, guestProfile also updates the user's profile. Requires the speakersWrite permission. - DELETE
/manage/events/:eventId/speakers/:entryIdRemove speaker (Admin)For 4S-user speakers, removes the speaker metadata and session links while keeping the registration. For guest speakers, the registration and the guest user are deleted as well. Requires the speakersWrite permission. - POST
/manage/events/:eventId/speakers/guestCreate guest speaker (Admin)Create a speaker who is not registered on 4S as a guest user (cannot sign in, no public profile), together with a draft (UNUSED) registration and speaker metadata. Requires the speakersWrite permission. Email is optional and no emails are sent to guests. - POST
/manage/events/:eventId/speakers/orderReorder speakers (Admin)Reorder speakers in bulk. Requires the speakersWrite permission. - POST
/manage/events/:eventId/speakers/avatarUpload speaker display avatar (Admin)Upload an avatar image for the event-specific display override and get its URL; pass it as overrides.avatarUrl. Requires the speakersWrite permission. - POST
/manage/events/:eventId/speakers/:entryId/guest-avatarUpdate guest speaker avatar (Admin)Update a guest user's profile image (the user's avatarUrl). Guests hold no display overrides, so the avatar override is cleared. Cannot be used for 4S-user speakers (ENTRY_NOT_GUEST). Requires the speakersWrite permission. - GET
/manage/events/:eventId/ticketsList tickets (Admin)List all ticket types with sales figures. Requires the ticketsRead permission. - POST
/manage/events/:eventId/ticketsCreate ticket (Admin)Create a ticket type. Requires the ticketsWrite permission. Paid tickets require price and paymentType, and the event must have Stripe Connect configured. - PUT
/manage/events/:eventId/tickets/:ticketIdUpdate ticket (Admin)Update a ticket type. Requires the ticketsWrite permission. price and paymentType cannot be changed once sales have started. - DELETE
/manage/events/:eventId/tickets/:ticketIdDelete ticket (Admin)Delete a ticket type. Tickets with existing registrations cannot be deleted. Requires the ticketsWrite permission. - POST
/manage/events/:eventId/tickets/orderReorder tickets (Admin)Reorder ticket types in bulk. Requires the ticketsWrite permission. - GET
/manage/events/:eventId/announcementsList event announcements (Admin)List event announcements for management. Requires the announcementsRead permission. - POST
/manage/events/:eventId/announcementsCreate event announcement (Admin)Create an announcement for participants. Requires the announcementsWrite permission. Use targetStates / targetTicketIds to narrow the audience (defaults to CONFIRMED + CHECKED_IN entrants across all tickets). sendEmail defaults to true, so omitting it sends email to the targeted recipients. - PUT
/manage/events/:eventId/announcements/:idUpdate event announcement (Admin)Update an announcement. Requires the announcementsWrite permission. - DELETE
/manage/events/:eventId/announcements/:idDelete event announcement (Admin)Delete an announcement. Requires the announcementsWrite permission. - GET
/manage/events/:eventId/questionsList survey questions (Admin)List all survey questions including drafts. Requires the questionsRead permission. - POST
/manage/events/:eventId/questionsCreate survey question (Admin)Create a registration survey question. Requires the questionsWrite permission. - PUT
/manage/events/:eventId/questions/:questionIdUpdate survey question (Admin)Update a survey question. Requires the questionsWrite permission. - DELETE
/manage/events/:eventId/questions/:questionIdDelete survey question (Admin)Delete a survey question; its answers are removed as well. Requires the questionsWrite permission. - PUT
/manage/events/:eventId/questions/ordersReorder survey questions (Admin)Reorder survey questions in bulk. Requires the questionsWrite permission. - PUT
/manage/events/:eventId/questions/bulkBulk update survey questions (Admin)Create or update multiple questions at once, as used when saving the survey editor. Requires the questionsWrite permission. - GET
/manage/events/:eventId/questions/:questionId/answersList answers to a question (Admin)List the answers submitted for a specific question. Requires the questionsRead permission. - GET
/manage/events/:eventId/survey-responsesList survey responses (Admin)List survey responses across all participants. Requires the questionsRead permission. - GET
/manage/events/:eventId/question-aggregationSurvey aggregation (Admin)Return aggregated answer counts for choice questions. Requires the questionsRead permission. - POST
/manage/events/:eventId/meeting-spot-imagesUpload meeting spot image (Admin)Upload an image for a meeting spot and get its URL. Requires the meetingSpotsWrite permission. - GET
/manage/events/:eventId/meeting-spotsList meeting spots (Admin)List all meeting spots including inactive ones. Requires the meetingSpotsRead permission. - POST
/manage/events/:eventId/meeting-spotsCreate meeting spot (Admin)Create a meeting spot. Requires the meetingSpotsWrite permission. - PUT
/manage/events/:eventId/meeting-spots/:spotIdUpdate meeting spot (Admin)Update a meeting spot. Requires the meetingSpotsWrite permission. - DELETE
/manage/events/:eventId/meeting-spots/:spotIdDelete meeting spot (Admin)Delete a meeting spot. Requires the meetingSpotsWrite permission. - POST
/manage/events/:eventId/meeting-spots/reorderReorder meeting spots (Admin)Reorder meeting spots in bulk. Requires the meetingSpotsWrite permission. - GET
/manage/events/:eventId/entriesList participants (Admin)List the event's registrations. Requires the participantsRead permission. Supports filtering by state, ticket and keyword. - GET
/manage/events/:eventId/entries/:entryIdParticipant detail (Admin)Retrieve a single registration including its survey answers. For events using invitation tickets it also includes the inviter (invitedBy) and the number of people invited (invitedCount / inviteLimit). Requires the participantsRead permission. - POST
/manage/events/:eventId/entriesAdd participant (Admin)Register a 4S user as a participant (the registration is created as CONFIRMED). Requires the participantsWrite permission. Fails with EVENT_ALREADY_REGISTERED if the user already has an active registration, and with USER_IS_GUEST for guest users. Capacity and ticket stock are checked like self-registration. - GET
/manage/events/:eventId/entries/countsParticipant counts (Admin)Return registration counts by state and ticket. Requires the participantsRead permission. - GET
/manage/events/:eventId/entries/by-checkin-tokenLook up participant by check-in token (Admin)Look up a registration by the check-in token embedded in the participant's QR code, for use in a reception app. Requires the participantsRead permission. - GET
/manage/events/:eventId/entries/:entryId/activity-logsParticipant activity logs (Admin)Return the history of state changes, check-ins and other operations for a registration, newest first. There is no pagination: every log for the registration is returned. Requires the participantsRead permission. - PUT
/manage/events/:eventId/entries/:entryId/stateChange entry state (check-in) (Admin)Change a registration's state: CHECKED_IN performs an on-site check-in, CANCELED cancels it, and for approval-based events CONFIRMED / REJECTED approve or reject it. Requires the participantsWrite permission. - PUT
/manage/events/:eventId/entries/:entryId/ticketChange a participant's ticket (Admin)Change the ticket type attached to a registration. No payment or refund of the price difference is performed. Requires the participantsWrite permission. - GET
/manage/events/:eventId/entries/invited-by/searchSearch inviters (Admin)Search for inviters to filter the participant list by. Keywords are split on whitespace and combined with AND; up to 50 options are returned. Requires the participantsRead permission. - POST
/manage/events/:eventId/stripe-connect/entries/:entryId/refundRefund a registration (Admin)Fully refund a registration paid via Stripe. The refund covers the remaining amount (paid minus already refunded) and no request body is required. Requires the paymentsWrite permission (ADMIN). - POST
/manage/events/:eventId/badge-printPrint a badge label (Admin)Print a badge label from a reception station on an event with the enavle integration enabled. Requires the participantsWrite or speakersWrite permission. - GET
/manage/events/:eventId/label-printRead label printing configuration (Admin)Read label printing configuration. - PUT
/manage/events/:eventId/label-printToggle label printing (Admin)Enable or disable Brother label printing for the event. Requires eventWrite. - POST
/manage/events/:eventId/label-print/printersRegister or update a printer (Admin)Register or update a printer. - DELETE
/manage/events/:eventId/label-print/printers/:printerIdDelete a printer registration (Admin)Requires eventWrite. Deletes the registration while retaining history and cancelling prepared or queued jobs. Returns 409 while receiving or with claimed, printing or unknown jobs. The same serial number can be registered again. - POST
/manage/events/:eventId/label-print/printers/:printerId/stationMaintain a shared station (Admin)Maintain a shared station. - POST
/manage/events/:eventId/label-print/printers/:printerId/claimClaim a print job (Admin)Claim a print job. - POST
/manage/events/:eventId/label-print/previewPreview a label without creating a print job (Admin)Requires authentication and event management permissions, plus eventWrite when entryId is omitted. Does not create a job, history entry or expiry. Pass the returned previewHash when creating a job on print. - GET
/manage/events/:eventId/label-print/jobsList print jobs (Admin)List print jobs. - GET
/manage/events/:eventId/label-print/jobs/:jobIdRead print job status (Admin)Read print job status. - POST
/manage/events/:eventId/label-print/jobsPrepare an immutable label (Admin)Prepare an immutable label. - GET
/manage/events/:eventId/label-print/jobs/:jobId/contentRead label image and raster (Admin)Read label image and raster. - POST
/manage/events/:eventId/label-print/jobs/:jobId/submitSubmit a prepared job (Admin)Submit a prepared job. - POST
/manage/events/:eventId/label-print/jobs/:jobId/transitionStart, report, cancel or resolve a job (Admin)Start, report, cancel or resolve a job. - GET
/manage/events/:eventId/dashboard/participants-summaryParticipant counts by state (Admin)Return registration counts grouped by state. All seven states are always present as keys, with 0 when there are none. Requires the participantsRead permission. - GET
/manage/events/:eventId/dashboard/participants-trendRegistration trend (Admin)Return the registration trend over a period. `data` holds per-bucket counts by state, and `total` is the cumulative number of confirmed registrations (CONFIRMED / CHECKED_IN). Requires the participantsRead permission. - GET
/manage/events/:eventId/dashboard/participants-analyticsParticipant demographics (Admin)Aggregate the profile answers captured at registration (gender, age group, residence). Only PENDING / AWAITING_PAYMENT / CONFIRMED / CHECKED_IN registrations are counted, and unanswered values are excluded. Requires the participantsRead permission. - GET
/manage/events/:eventId/dashboard-chartsDashboard survey aggregations (Admin)Return aggregations for the choice questions (singleSelect / multiSelect / toggle / radioButton) marked for dashboard display, counted over confirmed participants (CONFIRMED / CHECKED_IN). Requires the dashboardRead permission. - GET
/manage/events/:eventId/meetings/summaryMeeting summary (Admin)Return the number of meeting requests, confirmations, cancellations and messages for the event. Requires the meetingsRead permission (EDITOR or above).
Communities
- GET
/communitiesList / search communitiesSearch and list public communities. `tags` is AND-matched across all tags. - GET
/communities/:idCommunity detailRetrieve a community's detail. Returns 404 for private communities you cannot access. - GET
/communities/:id/playlistsCommunity playlistsRetrieve a community's public playlists (items not included). - GET
/communities/:id/playlists/:playlistIdCommunity playlist detailRetrieve a playlist that belongs to a community. - GET
/communities/:id/membersList community membersList community members. User objects contain public fields only and never include email. - GET
/communities/:id/members/countsCommunity member countsReturn member counts grouped by role and status. - GET
/communities/:id/eventsList community eventsList events organized by the community. - GET
/communities/:id/my-membershipOwn community membershipReturn the authenticated user's membership in the community, or null if not a member. - GET
/communities/:id/chat-roomCommunity chat roomGet the community's chat room, creating it if it does not exist. Members only. - POST
/communities/:id/joinJoin a communityJoin a community. With joinType FREE_JOIN the membership becomes APPROVED immediately; with APPROVAL_REQUIRED it becomes PENDING. INVITATION_ONLY communities cannot be joined this way. - DELETE
/communities/:id/leaveLeave a communityLeave a community. The last remaining admin cannot leave. - GET
/communities/:communityId/announcementsList community announcementsList community announcements. Members only. - GET
/communities/:communityId/announcements/:idCommunity announcement detailRetrieve a single community announcement. Members only.
Communities (Admin)
- GET
/manage/communities/:idCommunity detail (Admin)Retrieve a community for management. Admins only. - POST
/manage/communitiesCreate community (Admin)Create a community. The creator is automatically added as an ADMIN member. No additional permission is required beyond being signed in. - PUT
/manage/communities/:idUpdate community (Admin)Update a community. Admins only. - DELETE
/manage/communities/:idDelete community (Admin)Delete a community. Admins only. - POST
/manage/communities/:id/logoUpload community logo (Admin)Upload the community's logo image. - POST
/manage/communities/:id/logo-darkUpload dark-theme logo (Admin)Upload the logo image used in dark theme. - POST
/manage/communities/:id/card-photoUpload community business card photo (Admin)Upload a photo to use as the community's business card background as multipart/form-data (field name cardPhoto) and get the stored URL. The photo is auto-oriented, scaled down to 1600px on its longer side and converted to WebP. This call does not change the community; pass the returned URL as cardPhoto.url to PUT /manage/communities/:id to save it. Admins only. - GET
/manage/communities/:id/membersList community members (Admin)List members for management. Admins only. - POST
/manage/communities/:id/membersAdd community member (Admin)Add an existing user as a community member. Admins only. - PUT
/manage/communities/:id/members/:memberIdUpdate community member (Admin)Update a member's role, status and title. Approving a pending join request (PENDING → APPROVED) is also done here. Admins only. - DELETE
/manage/communities/:id/members/:memberIdRemove community member (Admin)Remove a member from the community. Admins only. - GET
/manage/communities/:id/members/countsCommunity member counts (Admin)Return member counts grouped by role and status. Admins only. - GET
/manage/communities/:communityId/announcementsList announcements (Admin)List community announcements for management. Admins only. - POST
/manage/communities/:communityId/announcementsCreate community announcement (Admin)Create a community announcement. sendEmail defaults to true, so omitting it notifies members by email. - PUT
/manage/communities/:communityId/announcements/:idUpdate community announcement (Admin)Update a community announcement. Admins only. - DELETE
/manage/communities/:communityId/announcements/:idDelete community announcement (Admin)Delete a community announcement. Admins only.
Playlists
- GET
/playlistsPlaylist listSearch and list public playlists. - GET
/playlists/:playlistIdPlaylist detailRetrieve a playlist's detail (items via a separate endpoint). - GET
/playlists/:playlistId/itemsPlaylist itemsRetrieve playlist items. `page` uses numbered pagination, `cursor` uses cursor pagination. groupByDate=true returns date-grouped results. - GET
/playlists/:playlistId/geojsonPlaylist map pinsReturns the 4S events in the playlist that have coordinates as a GeoJSON FeatureCollection. Not paginated; narrow the result with bbox. Filters have the same meaning as GET /playlists/:playlistId/items. - POST
/playlists/:playlistId/preview-externalPreview an external URL for submissionFetch title, description, image and schedules from an external URL's OG / JSON-LD for the submission form. Only available on playlists that accept submissions. - POST
/playlists/:playlistId/item-requestsSubmit an eventSubmit an event to a playlist. The item is created with status=PENDING and stays hidden until a community admin approves it. Approval and rejection are done through the /manage/playlists endpoints.
Playlists (Admin)
- GET
/manage/playlistsList playlists (Admin)List a community's playlists for management. The communityId query parameter is required and only that community's admins can access it. - GET
/manage/playlists/:playlistIdPlaylist detail (Admin)Retrieve a playlist for management. Only the community's admins can access it. - POST
/manage/playlistsCreate playlist (Admin)Create a playlist for a community. Only that community's admins can do this. - PATCH
/manage/playlists/:playlistIdUpdate playlist (Admin)Update a playlist. Only the fields you send are updated. - DELETE
/manage/playlists/:playlistIdDelete playlist (Admin)Delete a playlist. Only the community's admins can do this. - POST
/manage/playlists/preview-externalPreview an external URL (Admin)Fetch the OGP metadata (title, description, image) of a URL before adding it to a playlist as an external item. - POST
/manage/playlists/:playlistId/thumbnailUpload playlist thumbnail (Admin)Upload the playlist's thumbnail image. - GET
/manage/playlists/:playlistId/itemsList playlist items (Admin)List the items in a playlist for management. - POST
/manage/playlists/:playlistId/itemsAdd playlist item (Admin)Add a 4S event or an external link to the playlist. Set the type with kind: pass eventId for kind=FOUR_S_EVENT, or sourceUrl and title for kind=EXTERNAL_LINK. The display order is assigned automatically at the end. - PATCH
/manage/playlists/:playlistId/items/:itemIdUpdate playlist item (Admin)Update a playlist item's title, description or display order. - DELETE
/manage/playlists/:playlistId/items/:itemIdDelete playlist item (Admin)Remove an item from the playlist. - POST
/manage/playlists/:playlistId/items/:itemId/thumbnailUpload playlist item thumbnail (Admin)Upload the thumbnail image for a playlist item.
Chat
- GET
/chat-roomsList chat roomsList the chat rooms the authenticated user belongs to, including unread counts and the latest message. - GET
/chat-rooms/:idChat room detailRetrieve a chat room and its members. Only room members can access it. - POST
/chat-roomsCreate chat roomCreate a chat room. With two members (including yourself) it becomes a DIRECT room, otherwise a GROUP room. For DIRECT rooms an existing room is returned if one already exists. initialMessage is optional; passing it also creates the first message. A system message recording the creation is always added to a newly created room, whether or not initialMessage is given (not when an existing DIRECT room is returned). - PUT
/chat-rooms/:idRename chat roomRename a group chat. Only joined members can rename it. Direct messages (whose display name is derived from the participants) and event/community chats (which use their own name) cannot be renamed. Sending an empty string clears the name. - GET
/chat-rooms/:id/membersList chat room membersList a chat room's members with pagination. Includes members in the JOINED and INVITED states. Only rooms the authenticated user belongs to can be read. - POST
/chat-rooms/:id/membersInvite chat room membersInvite users to a chat room. Invitees enter the INVITED state and can join or decline. Only members in the JOINED state can invite. - DELETE
/chat-rooms/:id/members/:memberIdRemove a chat room memberRemove a member from a chat room. Only members in the JOINED state can remove others, and you cannot remove yourself (use leave instead). Not available for event or community chats. - GET
/chat-rooms/:id/mention-candidatesList mention candidatesList the people who can be @mentioned. Only users returned here are accepted in the mentions field when sending a message (the server applies the same check). For community chats the set is the approved community members, which differs from the members endpoint (ChatRoomMember rows). Event chats do not support mentions and always return an empty array. There is no pagination: results are capped by limit, so narrow them with q. - PUT
/chat-rooms/:id/joinJoin a chat roomAccept an invitation and join the chat room (INVITED → JOINED). - PUT
/chat-rooms/:id/declineDecline a chat room invitationDecline an invitation to a chat room. The membership record is deleted, so the room no longer appears in your list; you can join again if you are re-invited. - PUT
/chat-rooms/:id/leaveLeave a chat roomLeave a chat room (JOINED → LEFT). The membership record is retained with the LEFT status. - GET
/chat-rooms/:roomId/messagesList messagesList the room's messages using cursor-based pagination. Only room members can access it. - POST
/chat-rooms/:roomId/messagesSend a messagePost a message to a chat room. Send `content` as JSON for a text message, or a `file` as multipart/form-data for an image message. Only room members can post. - DELETE
/chat-rooms/:roomId/messages/:messageIdDelete a messageSoft-delete a message you sent. Deleted messages are excluded from listings, unread counts and the image list. Deleting an already deleted message succeeds. - GET
/chat-rooms/:roomId/messages/:messageId/reactionsList who reactedList the reactions on a message together with the users who added them. The message list only includes userIds, so use this endpoint when you need to show who reacted. - POST
/chat-rooms/:roomId/messages/:messageId/reactionsAdd a reactionAdd an emoji reaction to a message. One user can add several different emojis. Sending the same emoji twice does not add a duplicate (idempotent). Only emojis from a fixed allow list are accepted. - DELETE
/chat-rooms/:roomId/messages/:messageId/reactionsRemove a reactionRemove your own reaction. Succeeds even if you had not reacted (idempotent). The emoji is passed as a query parameter. - GET
/chat-rooms/:roomId/imagesList room imagesList images uploaded to the chat room, newest first. Images from deleted messages are excluded. - PUT
/chat-rooms/:roomId/notificationsToggle room notificationsTurn notifications (push, in-app and email) on or off for this chat room. Unread counts are unaffected — the badge still increases, you just are not notified. The current value is available as notificationsEnabled on your member entry in the room detail response. - GET
/chat-rooms/:roomId/filesList filesList the chat room's file library. Folders and files are returned in a single items array sorted by name regardless of type, together with breadcrumbs. Omit folderId for the root. There is no pagination: the whole contents of the folder are returned. - POST
/chat-rooms/:roomId/filesRegister an uploaded fileRegister a file that was PUT to S3. The server verifies the real size and rejects anything over 10MB. If a file with the same name exists in the folder, it is numbered as "report (2).pdf". Set postToChat to true to also post it to the chat. - PUT
/chat-rooms/:roomId/files/:fileIdRename or move a fileRename a file and/or move it to another folder. If the destination already has a file with that name, the name is numbered. - DELETE
/chat-rooms/:roomId/files/:fileIdDelete a fileSoft-delete a file you uploaded. Deleting an already deleted file succeeds. - POST
/chat-rooms/:roomId/files/upload-urlCreate an upload URLFiles are uploaded straight to S3, not through the API. PUT the file to the returned uploadUrl with exactly the requiredHeaders. Do not add an Authorization header (it breaks the signature). Register the upload afterwards with POST /chat-rooms/:roomId/files. The limit is 10MB. - GET
/chat-rooms/:roomId/files/:fileId/urlCreate a download URLFiles are private, so fetch a short-lived URL each time. disposition=inline is honoured only for allowed types such as PDF; anything else is forced to attachment. The URL carries the file name, so opening it as a link downloads the file. - GET
/chat-rooms/:roomId/foldersList foldersList every folder in the room as a flat array without pagination. Use it to build a tree for destination pickers. - POST
/chat-rooms/:roomId/foldersCreate a folderCreate a folder. Folders can nest up to 10 levels. Two folders in the same parent cannot share a name (unlike files, folders are not numbered). - PUT
/chat-rooms/:roomId/folders/:folderIdRename or move a folderRename a folder and/or move it. A folder cannot be moved into itself or one of its descendants, and a move that would exceed 10 levels is rejected. - DELETE
/chat-rooms/:roomId/folders/:folderIdDelete a folderSoft-delete a folder together with its files and subfolders. Only the creator can delete it, and the request is rejected if it contains files uploaded by others. The returned counts can be used in a confirmation dialog. - PUT
/chat-rooms/:roomId/readMark messages as readMark the room's messages as read, resetting the unread count to zero.
Notifications
- GET
/notificationsList notificationsList notifications addressed to the authenticated user, newest first. Deleted notifications are excluded. With `unreadFirst=true`, unread notifications come first and are ordered newest-first within that group (useful for a header dropdown). - GET
/notifications/unread-countUnread notification countReturn just the authenticated user's unread notification count. A lightweight endpoint for rendering a badge. - PUT
/notifications/:id/readMark a notification as readMark the given notification as read. Idempotent — calling it on an already-read notification succeeds. Returns 404 for notifications owned by another user or already deleted. - PUT
/notifications/read-allMark all notifications as readMark every unread notification of the authenticated user as read. Returns how many were updated.
Push notifications
- GET
/push/devicesList push devicesList the push devices the authenticated user registered through this app, newest first. Disabled devices are excluded. - POST
/push/devicesRegister a push deviceStore an FCM registration token so the user can receive push notifications for new chat messages. Registration is idempotent on token, so re-sending the same token does not create duplicates. If the token already belongs to another user (device hand-off or account switch), ownership moves to the authenticated user. A previously disabled token is re-enabled. - DELETE
/push/devices/:deviceIdUnregister a push deviceStop push notifications for the given device. Call this on sign-out. Returns 404 for devices registered by another app or owned by another user.
Bookmarks
- GET
/bookmarksBookmark listRetrieve events the authenticated user has bookmarked, newest first. - POST
/bookmarksAdd a bookmarkAdd an item to your bookmarks. The operation is idempotent: bookmarking an already-bookmarked item is not an error. For EVENT the target is checked for existence and 404 is returned if it does not exist. - GET
/bookmarks/idsBookmarked ID listRetrieve the array of bookmarked entity IDs for the authenticated user, newest first. - DELETE
/bookmarks/:entity/:entityIdRemove a bookmarkRemove a bookmark. Removing a bookmark that does not exist is not an error.
Misc
- GET
/slug/:slugResolve resource by slugResolve the resource (user / organization / community / event) for a slug. Matched in order: user → organization → community → event. - GET
/tags/masterTag suggestionsReturns the curated tag candidates. Use them to suggest values in tag inputs such as the skill tags on a profile. Sorted by `order` ascending, then by name. - GET
/tags/usageTags in useAggregates the tags actually attached to records, grouped by entity and category. Useful for search filter suggestions. - GET
/places/autocompletePlace autocompleteReturns place suggestions for the text being typed. The Google Places API is called on the server, so clients do not need an API key. Used for the location field on a profile. - GET
/places/:placeIdPlace detailReturns an object for the given placeId in the shape used to store a user's location. The Japanese rendering is included under languages.ja.