Axuz Docs

Public area and external assignees

How to publish boards, files, forms and flowcharts through public links (capability URLs), what an anonymous visitor can do, and how to assign tasks to people outside the workspace.

Some API routes do not require a login: whoever holds the link gets in. This page explains the model (capability URLs), the five public surfaces that exist today, how to create and revoke links, what an anonymous visitor can not do, and how to assign a task to someone who is not a workspace member.

A public link carries the credential in its path: /public/shares/<token>. There is no cookie and no Authorization header; the token is the permission. Therefore:

  • the token is generated by a CSPRNG with 32 bytes in base64url (43 characters) and cannot be guessed;
  • it is shown only once, at creation (for the public board the server stores only its SHA-256);
  • whoever receives the link can forward it. Treat it like a password: keep it out of logs, issues and analytics;
  • revoking the link takes effect immediately.

Public surfaces

SurfaceHostRoutes without loginWho creates the link
Read-only board (kanban)api-workspaceGET /public/shares/{token}team owner/admin
Drive filefiles-apiGET /share/{token}, /info, POST /verify, /preview, /downloadfile uploader or owner/admin
Published formapi-formsGET /public/forms/{slug}, /render, POST /submit, POST /pre-submitwhoever publishes the form
Flowchartapi-flowGET /public/shared/{token} (and sub-routes)member with access to the flowchart
Public meeting roommeet-apiPOST /rooms/{id}/anonymous-token, POST /rooms/by-slug/{slug}/anonymous-tokenroom with visibility=public

Hosts are https://<service>.axuz.net (staging) and https://<service>.axuz.club (production). See API concepts.

Public board (read-only)

Create, list and revoke

These routes require a session and the team owner or admin role. A regular member who can see the board gets 403; someone who cannot see the board (another tenant, closed channel) gets 404.

POST/teams/{teamId}/boards/{id}/public-shares GET/teams/{teamId}/boards/{id}/public-shares DELETE/teams/{teamId}/boards/{id}/public-shares/{shareId}

curl

curl -sS -X POST "https://api-workspace.axuz.net/teams/$TEAM_ID/boards/$BOARD_ID/public-shares" \
  -H "Authorization: Bearer $AXUZ_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"show_assignees": true, "show_due_dates": true, "expires_at": "2026-12-31T23:59:59Z"}'

Body (all optional; an unknown field is 400):

FieldDefaultEffect
show_assigneesfalseshows assignees (display name only)
show_due_datesfalseshows due dates
show_descriptionsfalseshows board and task descriptions
expires_atnever expiresa future date, at most 365 days ahead

The 201 response carries the metadata and the token field, in this response only. The list call never returns the token: if you lost it, revoke and create another. Limit of 20 active links per board (422 too many active shares). Revoking is idempotent (204 even if already revoked).

Reading as an anonymous visitor

GET/public/shares/{token}
curl -sS "https://api-workspace.axuz.net/public/shares/$SHARE_TOKEN"

The response is a dedicated, minimal shape: name, description, columns[] with tasks[] (title, description, priority, due_at, is_all_day, assignees[]). E-mails, user ids, comments, attachments, custom fields, labels, checklists and task/board/team ids are never returned.

Security of this route:

  • a nonexistent, malformed, expired or revoked token, or one for a deleted board, always returns the same 404 {"error":"not_found"}, so nothing reveals which case happened (anti-enumeration);
  • per-IP rate limit, default 60 requests/min, 429 with Retry-After;
  • every response carries Cache-Control: no-store, X-Robots-Tag: noindex, nofollow and Referrer-Policy: no-referrer;
  • no open CORS: the front end consumes it from the same origin (BFF). Calling it from JavaScript on another site is not supported.

Drive file (password, expiry, download)

The link creator (session required) sets the options. This is the only surface with a password.

POST/teams/{teamId}/files/{fileId}/share-links
curl -sS -X POST "https://files-api.axuz.net/teams/$TEAM_ID/files/$FILE_ID/share-links" \
  -H "Authorization: Bearer $AXUZ_TOKEN" -H "Content-Type: application/json" \
  -d '{"password": "'"$LINK_PASSWORD"'", "allow_download": true, "expires_in_hours": 48}'

expires_in_hours ranges from 1 to 8760 (365 days); 0 or absent means no expiry. allow_download defaults to false (view only). The response carries token and public_url; only the link’s creator can see the token. To revoke: DELETE /teams/{teamId}/files/{fileId}/share-links/{shareLinkId}.

The visitor uses:

  1. GET /share/{token}/info: name, size, type, has_password, allow_download, expires_at.
  2. If has_password, POST /share/{token}/verify with {"password": "..."} returns a session_token (valid for 1 hour). A wrong password is 401 and does not increment the access counter.
  3. GET /share/{token}/preview?session=<jwt> (inline) or GET /share/{token}/download?session=<jwt> (attachment; requires allow_download, otherwise 403). The session can also go in the X-Share-Session header.
SESSION=$(curl -sS -X POST "https://files-api.axuz.net/share/$FILE_TOKEN/verify" \
  -H "Content-Type: application/json" -d '{"password": "'"$LINK_PASSWORD"'"}' | jq -r .session_token)
curl -sS -L -o file.bin "https://files-api.axuz.net/share/$FILE_TOKEN/download?session=$SESSION"

Unlike the board, a revoked or expired link answers 410 Gone and an unknown one 404: the files service distinguishes the cases. No rate limit specific to /share/* is documented beyond the service’s general protection; not documented yet.

Published form

Published forms are reached by slug, not by token. They need no login and no specific origin (they can be embedded on any site).

curl -sS "https://api-forms.axuz.net/public/forms/$FORM_SLUG"
curl -sS -X POST "https://api-forms.axuz.net/public/forms/$FORM_SLUG/submit" \
  -H "Content-Type: application/json" \
  -d '{"name": "Maria", "email": "maria@example.test"}'

Verified in the code: rate limit of 10 submissions per minute per IP and form (429); the axuz_hp honeypot field when enabled on the form; optional recaptcha_token/turnstile_token verified in the background; field validation returns 422 with fields; success returns 201 immediately (spam is flagged afterwards and the visitor sees no difference). The body can be JSON or application/x-www-form-urlencoded. pre-submit saves a partial draft. An unpublished or unknown form is 404.

Shared flowchart

The owner creates links with POST /teams/{teamId}/flowcharts/{id}/shares using mode (public, time, email), access (view, interact), expires_at, target_email (email mode) and max_views. The visitor reads GET /public/shared/{token}, which returns title, document, readonly and share. In email mode the visitor must be logged in as the target e-mail (403 with requires_login). There is a rate limit of 240 requests/min per token, and the link auto-revokes when max_views is reached. Links with access=interact allow sending edits through the /ops, /document and /draft sub-routes; see the reference for the full contract.

Public meeting room (guest)

For rooms with visibility=public, anyone can obtain a join token by giving a name:

curl -sS -X POST "https://meet-api.axuz.net/rooms/by-slug/$ROOM_SLUG/anonymous-token" \
  -H "Content-Type: application/json" -d '{"guest_name": "Guest"}'

Response: join_url, jwt, expires_at and guest_id. guest_name is required (400); the guest is never a moderator. A non-public room answers 404, the same as a missing room.

What an anonymous visitor can and cannot do

CanCannot
Read the published board within the scope the owner choseWrite, comment, move tasks
View/download the file (if allow_download)List other files, see the owner or the team
Submit a form responseRead earlier responses
Join the public room with a guest nameModerate or enter a private room
View the flowchart; edit only if access=interactList or view other flowcharts

External assignees

An external assignee is a person outside the workspace assigned to a task, for presentation purposes (for example, showing a client who on their side is responsible). In the current code it is only a label: name (1 to 80 characters), optional organization and show_on_public.

POST/teams/{teamId}/tasks/{id}/external-assignees GET/teams/{teamId}/tasks/{id}/external-assignees DELETE/teams/{teamId}/tasks/{id}/external-assignees/{assigneeId}
curl -sS -X POST "https://api-workspace.axuz.net/teams/$TEAM_ID/tasks/$TASK_ID/external-assignees" \
  -H "Authorization: Bearer $AXUZ_TOKEN" -H "Content-Type: application/json" \
  -d '{"name": "Ana Souza", "organization": "Example Client", "show_on_public": true}'

Any team member with access to the channel’s tasks module can add or remove. Limit of 25 active externals per task (422). Removal is idempotent (204) and the name disappears from the public board immediately. Events emitted: task.external_assignee.added and task.external_assignee.removed. On the public board the external appears in assignees[] with external: true and the organization, provided show_assignees is on for the link and show_on_public is true.

Common errors and what not to do

  • Error format: the workspace service answers {"error": "<phrase>"}, not RFC 9457.
  • 403 when creating a public link: you are a member but not a team owner/admin.
  • 422 too many active shares: revoke old links (maximum 20 per board).
  • 400 invalid expires_at: the date must be in the future and within 365 days.
  • Do not call /public/shares/{token} from JavaScript on another site: there is no CORS.
  • Do not log public URLs in application logs or send them through channels that generate indexable previews.
  • Do not use real people’s e-mails in tests; use @example.test.

Updated