Skip to content

Channel delegation (Sharing)

Delegation (sharing) is a way to give another person access to one of your channels without letting them into the rest of the account. A colleague will get either the right to only read messages from that channel, or to read and send new ones — but will not be able to see your other channels, change settings, or manage clients and tokens.

Useful when:

  • you need to show the team only the Prod-alerts channel without exposing the whole project;
  • a CI bot only needs to send to one channel (the send permission) and nothing else;
  • the on-call person should see incoming notifications for a specific service (view);
  • you want to grant access temporarily — a delegation can be easily revoked without touching the channel’s tokens.

Technically, each delegation is a separate Share token with the S prefix, bound to a channel. The channel owner issues the token, optionally specifying the recipient’s email and the permission level. The recipient uses the token like a regular notifly token (see below How the recipient uses the access).

permissionWhat is allowed
viewSee the channel in the list and read its messages (GET /message).
sendSame as view, plus sending messages (POST /message).

send includes view. Any mutations of the channel itself, clients, tokens, as well as deleting messages using a Share token are forbidden (response 403).

Separately from the access level there is the right to share further (canShare): it is off by default, and the recipient cannot hand your channel out to others — see Re-sharing.

A delegation to a specific email is first created as an invitation which the recipient can accept or decline:

statusWhat it means
pendingThe invitation has been issued; the recipient has not yet reacted.
acceptedThe recipient accepted — the channel has appeared in their list.
declinedThe recipient declined the invitation.

Only a pending invitation can be accepted (POST /share/:id/accept); attempting to accept/decline an already processed one will return 400 share is not pending.

The request is made by the channel owner (or a delegated user — see Re-sharing) with an API token or a device token with write permission.

Окно терминала
curl -X POST "$NOTIFLY_URL/application/42/share" \
-H "Content-Type: application/json" \
-H "X-Notifly-Key: <client-token>" \
-d '{
"recipientEmail": "colleague@example.com",
"permission": "view",
"note": "On-call this week"
}'

Request body fields (ChannelShareParams):

FieldTypeRequiredDescription
permissionstringyesview or send. Any other value → 400.
recipientEmailstringnoRecipient’s email. If empty — becomes an “open share” (see below).
canShareboolnoAllow the recipient to share the channel further (default false).
notestringnoArbitrary comment for yourself (e.g. “for CI”, “for colleague X”).

Response — the created ChannelShare object:

{
"id": 1007,
"token": "S9f3kQw7Zx2Lm5Np8Rt1Vy4",
"appid": 42,
"recipientEmail": "colleague@example.com",
"permission": "view",
"canShare": false,
"status": "pending",
"note": "On-call this week",
"created": "2026-06-23T10:00:00Z",
"lastUsed": null,
"appName": "Prod-alerts"
}

The token field (prefix S) is the Share token. Give it to the recipient by any channel, or rely on them finding the invitation via incoming.

If the addressee does not have a Notifly account yet, they get an invitation email with a link to sign up with that address; after signing up the invitation appears in their incoming list. Repeating the POST for the same address re-sends the email (such a grant has canResendInvite: true in the response).

Granting again to someone who already has access to the channel does not create a second row but changes the existing one: it raises the level to send, changes canShare, returns a declined invitation to pending, and restores revoked access — with a new token value.

If recipientEmail is left empty, the delegation becomes an open share — a link/token that anyone who has it can use. In this case the recipient joins via POST /share/join, and a personal copy of the delegation (with its own Share token) is created for them, leaving the original untouched. This is convenient for invitations like “forward the link to the team”.

After the delegation is accepted (accepted), the channel appears in the recipient’s normal list (GET /application). Then the Share token is used like any notifly token:

Окно терминала
# Read the channel's messages (view and send)
curl "$NOTIFLY_URL/message" -H "X-Notifly-Key: S9f3kQw7Zx2Lm5Np8Rt1Vy4"
# Send to the channel (send only)
curl -X POST "$NOTIFLY_URL/message" \
-H "X-Notifly-Key: S9f3kQw7Zx2Lm5Np8Rt1Vy4" \
-H "Content-Type: application/json" \
-d '{"title": "Deploy", "message": "Done"}'

A Share token grants access strictly to a single channel. Attempts to call mutating endpoints (create/modify a channel, delete a message, manage tokens) will return 403 channel tokens cannot perform this action.

The recipient sees not-yet-accepted (pending) invitations to their email via GET /share/incoming. Invitations to channels the recipient owns and invitations with the same permissions as an already accepted delegation are not shown; an invitation with different permissions is shown — it is an offer to change the access level.

Окно терминала
curl "$NOTIFLY_URL/share/incoming" -H "X-Notifly-Key: <client-token>"
Окно терминала
# Accept — the channel will appear in the recipient's list
curl -X POST "$NOTIFLY_URL/share/1007/accept" -H "X-Notifly-Key: <client-token>"
# Decline
curl -X POST "$NOTIFLY_URL/share/1007/decline" -H "X-Notifly-Key: <client-token>"

Only the user whose email matches the delegation’s recipientEmail can accept/decline; otherwise — 404. Only a pending invitation can be processed.

If the recipient was given the Share token directly (for example, an open share or simply the S... string), they join with a single call:

Окно терминала
curl -X POST "$NOTIFLY_URL/share/join" \
-H "Content-Type: application/json" \
-H "X-Notifly-Key: <client-token>" \
-d '{"token": "S9f3kQw7Zx2Lm5Np8Rt1Vy4"}'

Behavior depends on the type of delegation:

  • Named (with recipientEmail): the token joins only if the user’s email matches the delegation’s address (otherwise 403 this share is not for your email). The status is automatically changed to accepted.
  • Open share (without email): a personal copy of the delegation is created for the joiner with status accepted. If they already have a delegation for this channel, it is upgraded to send if necessary (and gets canShare if the link grants it) and set to accepted. If the owner revoked their access to the channel, they cannot come back via the link: 403 access to this channel was revoked by the owner.

An invalid, revoked or expired token → 404 invalid share token. The channel owner cannot join their own channel → 409 you already own this channel.

Managing issued delegations (for the owner)

Section titled “Managing issued delegations (for the owner)”

List of delegations for a specific channel. The channel owner sees all grants, including those made by recipients through re-sharing (other people’s grants have createdByEmail filled in); a recipient with canShare sees only their own:

Окно терминала
curl "$NOTIFLY_URL/application/42/share" -H "X-Notifly-Key: <client-token>"

Change the permission level, the right to share further, the email or the note — PUT /share/:id (channel owner only). If the level of an accepted delegation changes, the recipient gets a notification (“Your access was raised” / “Channel access changed”):

Окно терминала
curl -X PUT "$NOTIFLY_URL/share/1007" \
-H "Content-Type: application/json" \
-H "X-Notifly-Key: <client-token>" \
-d '{"permission": "send", "canShare": false, "recipientEmail": "colleague@example.com"}'

Revoke a delegation — DELETE /share/:id. This can be done by its creator or by the channel owner. Revocation is two-step:

  • the first DELETE on an active delegation revokes it: the token stops working immediately and the channel disappears from the recipient’s list, while the row stays in the list (with revokedAt) and acts as a block — the recipient cannot come back via a still-live open link. Together with the access, the recipient’s “ownerless” grants for this channel are revoked too: open links and unaccepted invitations;
  • a repeated DELETE on an already revoked row deletes it for good — together with the block. To close entry via an open link for good, delete the link itself.

Revoked access can be restored by granting again to the same addressee — the token will be new.

Окно терминала
curl -X DELETE "$NOTIFLY_URL/share/1007" -H "X-Notifly-Key: <client-token>"

To avoid typing emails manually, the sharing form can suggest addresses you’ve already shared with — GET /share/recipients returns a list of distinct emails from newest to oldest:

Окно терминала
curl "$NOTIFLY_URL/share/recipients" -H "X-Notifly-Key: <client-token>"

Leaving and rotating your own token (for the recipient)

Section titled “Leaving and rotating your own token (for the recipient)”

The recipient manages only their own side of the access — with two calls. Both take the channel in the request body (appid) rather than in the path:

Окно терминала
# Give up access to the channel (it disappears from the list, the token dies)
curl -X POST "$NOTIFLY_URL/share/leave" \
-H "Content-Type: application/json" \
-H "X-Notifly-Key: <client-token>" \
-d '{"appid": 42}'
# Change the value of your own Share token (for example, if it leaked)
curl -X POST "$NOTIFLY_URL/share/rotate" \
-H "Content-Type: application/json" \
-H "X-Notifly-Key: <client-token>" \
-d '{"appid": 42}'
  • share/leave removes all of your accesses to that channel (there may be several if different people shared it). To come back you need a new invitation — or a still-valid open link.
  • share/rotate changes the token value in place, keeping the permissions and the binding; the old value stops working immediately. All of your accepted and valid delegations for that channel are rotated; a revoked one cannot be rotated.

If you have no access to the channel, both calls return 404.

Email delegations are about people. For scripts, CI and integrations a channel has machine keys: several keys per channel, each with its own permissions, label, expiry and its own lastUsed.

Окно терминала
# Issue a send-only key
curl -X POST "$NOTIFLY_URL/application/42/token" \
-H "Content-Type: application/json" \
-H "X-Notifly-Key: <client-token>" \
-d '{"name": "CI deploy key", "canRead": false, "canWrite": true,
"note": "gitlab runner", "expiresAt": 0}'
FieldMeaning
namehuman-readable label of the key
canReadreading the channel history and subscribing over WebSocket
canWritesending to the channel (POST /message, /ask)
notefree-form comment
expiresAtexpiry, unix milliseconds; 0 — never

A key must allow at least one of canRead/canWrite, otherwise 400. Key management is available only to the channel owner: a delegation recipient does not get it even with write permission.

Revocation has two steps. The first DELETE /token/:id is soft: the key stops authenticating, but the row remains, so you can still see when it was last used and when it was revoked. A repeated DELETE removes the record permanently.

Rotation (POST /token/:id/rotate) changes only the value: the id, the permissions and the channel binding are preserved, so nothing that references the key breaks.

Revocation takes effect from the next request. The exception is an already open WebSocket connection: it was authorised before the upgrade and lives with the old key until it reconnects.

Only a recipient whom the owner granted the canShare right can share the channel further; without it POST /application/:id/share returns 403 you are not allowed to share this channel further. Even with this right you cannot grant more than you have yourself. If you only have view, attempting to grant send will return:

403 cannot grant 'send' permission — you only have 'view' access

The canShare right itself can likewise be passed on only by someone who has it.

The channel owner is always considered to have maximum access (send and canShare). In the GET /application/:id/share list a re-sharer sees only their own grants, while the owner sees all of them, with who made each one (createdByEmail), and can revoke any of them.

Method and pathAuthorizationPurpose
GET /application/:id/shareclient-tokenlist of channel delegations (only your own)
POST /application/:id/shareclient-token (write)create a delegation (issue a Share token)
PUT /share/:idclient-token (write)change permission / email / note (channel owner)
DELETE /share/:idclient-token (write)revoke a delegation
GET /share/incomingclient-tokenincoming invitations to the user’s email
GET /share/recipientsclient-tokenemail addresses you’ve already shared with (for suggestions)
POST /share/:id/acceptclient-tokenaccept a pending invitation
POST /share/:id/declineclient-tokendecline a pending invitation
POST /share/joinclient-tokenjoin a channel by Share token
POST /share/leaveclient-token (write)give up access to a channel, body {"appid": N}
POST /share/rotateclient-token (write)change your own Share token value, body {"appid": N}
GET /application/:id/tokenclient-tokenmachine keys of the channel (owner only)
POST /application/:id/tokenclient-token (write)issue a machine key
PUT /token/:idclient-token (write)change permissions, label and expiry
DELETE /token/:idclient-token (write)revoke a key (a repeated call deletes it permanently)
POST /token/:id/rotateclient-token (write)change the key value, keeping its permissions