Meeting link endpoints

Create, replace and turn off a channel's meeting link, remove guests from the room, and the public routes a guest uses to join without an account.

Updated

New in November 2026

Paths are relative to https://api.fishcamp.space/api/v1. The channel routes need the X-Workspace-Id header and a person: API keys get 403 forbidden on all of them, reading the link included. The /meet routes are public: guests call them without an account. How meeting links work in the app is in Meeting links for guests.

{
  "id": "c1d2e3f4-a5b6-4c7d-8e9f-0a1b2c3d4e5f",
  "code": "Hq7kT2mZpX4rNa",
  "url": "https://fishcamp.space/m/Hq7kT2mZpX4rNa",
  "channelId": "0b6f3c55-2a51-4e0a-a3d6-6a3f1f2e9d10",
  "createdBy": { "id": "7a1c2e3f-4b5d-4e6f-8a9b-0c1d2e3f4a5b", "name": "Maya Lopes" },
  "guestJoins": 3,
  "lastUsedAt": "2026-11-05T15:04:02.000Z",
  "createdAt": "2026-11-05T14:55:00.000Z"
}
FieldDescription
code14 letters and digits: the secret part of the link.
urlThe link to send to guests.
createdByWho created it, or null.
guestJoinsHow many times guests joined with this link.
lastUsedAtWhen a guest last joined with it, or null.
GET /channels/:channelId/meeting-link

People who can see the channel. The channel's active link, or null when it has none.

POST /channels/:channelId/meeting-link

People who can see the channel. Returns 200 with the channel's active link, and creates it the first time. A channel has one link at a time, so calling this again returns the same link.

curl -X POST "https://api.fishcamp.space/api/v1/channels/0b6f3c55-2a51-4e0a-a3d6-6a3f1f2e9d10/meeting-link" \
  -H "Authorization: Bearer YOUR_SESSION_TOKEN" \
  -H "X-Workspace-Id: YOUR_WORKSPACE_ID"
ErrorWhen
409 voice_disabledThe channel's voice is off.
422 validation_errorIt's a direct message.
404 not_foundYou can't see the channel, or it was archived.
POST /channels/:channelId/meeting-link/reset

People. Turns off the current link and returns a new one (200). The old code stops working at once, and the guests in the room are taken out of it. The channel's voice must be on (409 voice_disabled otherwise).

DELETE /channels/:channelId/meeting-link

People. Returns 204, or 404 when the channel has no active link. The guests in the room are taken out of it, and guests who open the old link get 410 meeting_link_revoked.

Remove a guest from the room

POST /channels/:channelId/call/guests/:identity/remove

People who can see the channel. Takes a guest out of the audio room and returns 204. A guest's identity starts with guest_: find it in GET /channels/:id/call.

ErrorWhen
422 validation_errorThe identity isn't a guest's. People from the workspace can't be removed this way.
404 not_foundThat guest isn't in the room.
503 media_unavailableThe audio rooms didn't answer. Try again in a moment.

The link keeps working. To keep the guest out, replace it.

Public routes for guests

The guest page at https://fishcamp.space/m/<code> calls these routes for you. Use them only to build your own guest client.

Preview a meeting

GET /meet/:code

No token. Limit: 60 per minute.

{
  "workspaceName": "Northstar Studio",
  "channelName": "design-crit",
  "status": "active",
  "hostPresent": true,
  "participantCount": 2,
  "cameraEnabled": true,
  "screenShareEnabled": true,
  "transcriptionEnabled": true
}
FieldDescription
statusactive; revoked when the link was turned off or replaced; unavailable when the channel's voice is off or the channel was archived.
hostPresentWhether someone from the workspace is in the room. Guests can only join while it's true.
participantCountEveryone in the room now, guests included.
cameraEnabled, screenShareEnabledWhat guests may use, following the channel's switches.
transcriptionEnabledWhether what guests say is transcribed. Tell them before they join.

An unknown code answers 404.

Join

POST /meet/:code/join

No token. Limit: 10 per minute.

FieldTypeRequiredDescription
namestringYesHow the team sees the guest. Control characters are removed, spaces are collapsed and the name is cut to 40 characters.
curl -X POST "https://api.fishcamp.space/api/v1/meet/Hq7kT2mZpX4rNa/join" \
  -H "Content-Type: application/json" \
  -d '{"name": "Ana Souza"}'
{
  "url": "wss://…",
  "token": "eyJhbGciOi…",
  "roomName": "ch_0b6f3c55-2a51-4e0a-a3d6-6a3f1f2e9d10",
  "identity": "guest_q7Rk2mTzX9pLw4Hn",
  "name": "Ana Souza",
  "guestToken": "eyJhbGciOi…",
  "expiresAt": "2026-11-05T21:04:02.000Z",
  "cameraEnabled": true,
  "screenShareEnabled": true,
  "transcriptionEnabled": true
}
  • Connect to the room with any LiveKit client SDK, using url and token, within 10 minutes. The token only allows what the channel's switches allow, and never data messages.
  • Fishcamp checks the guest again when they connect: if nobody from the workspace is in the room, the link was turned off, or the room already has 20 guests, the guest is taken out.
  • When the last person from the workspace leaves, the room closes for the guests too.
  • guestToken sends the guest's audio to transcription (below). It lasts 6 hours, until expiresAt, and opens nothing else: the rest of the API answers 401 to it.
ErrorWhen
409 waiting_for_hostNobody from the workspace is in the room yet. Check GET /meet/:code every few seconds and try again when hostPresent is true.
409 meeting_fullThe room already has 20 guests.
409 meeting_unavailableThe channel's voice is off, or the channel was archived.
410 meeting_link_revokedThe link was turned off or replaced.
422 validation_errorThe name is missing or empty.
503 media_unavailableAudio rooms aren't available on this server.

Send audio for transcription

POST /meet/:code/transcript?startedAt=1793891052000&durationMs=15000

Sends a chunk of the guest's own microphone while they're in the room and the channel's transcription is on. Fishcamp turns it into text and discards the audio. The last chunk still counts if it arrives up to 18 seconds after the guest left. Limit: 12 per minute.

  • Header: Authorization: Bearer <guestToken>.
  • Body: the raw audio, from 200 bytes to 1 MB, with a Content-Type of audio/webm, audio/ogg, audio/mp4, audio/mpeg or audio/wav.
  • Query: startedAt, when the chunk started, in Unix milliseconds (within 24 hours of now), and durationMs, from 500 to 120,000.

The answer is the new transcript line, with the guest's name marked, or { "skipped": true } when the chunk had no speech:

{
  "id": "b7a6c5d4-e3f2-4a1b-9c8d-7e6f5a4b3c2d",
  "mediaSessionId": "e4d3c2b1-a0f9-4e8d-b7c6-5a4b3c2d1e0f",
  "channelId": "0b6f3c55-2a51-4e0a-a3d6-6a3f1f2e9d10",
  "profileId": null,
  "speakerName": "Ana Souza (guest)",
  "startMs": 240000,
  "endMs": 255000,
  "text": "Send me the preview link when it's ready."
}
ErrorWhen
401 unauthorizedThe guest token is missing, invalid or expired.
410 meeting_link_revokedThe link was turned off or replaced after the guest joined.
403 forbiddenThe guest isn't in the room, and didn't leave it in the last 18 seconds.
409 transcription_offThe channel's transcription is off.
409 no_audio_roomNo audio room is open in the channel.
409 transcript_closedThis room's transcript is already closed.
422 validation_errorThe body, its type, startedAt or durationMs isn't valid.
502 transcription_failed, 503 transcription_unavailableSpeech-to-text failed for this chunk, or isn't available right now.

On this page