Messaging and contacts
Send email to contacts, manage them, and configure Slack notifications.
A space can email the people it corresponds with, keeps them as contacts, and can post Slack notifications when things happen. This page covers all of it.
See Authentication for the role each level needs.
Replace https://cadence.example.com with your instance's address.
Send a message
POST /api/v1/messages; MCP messages_send. Needs member.
Address the recipient by email, and give a subject and a markdown body. You
can also pass profile fields — first_name, last_name, language, timezone —
and an optional task_key to tie the message to a task. Over MCP, name the space
with space_key. On the REST endpoint you can also pass an idempotency_key to
make the send safe to retry; the MCP tool does not accept it and rejects the field.
curl -X POST https://cadence.example.com/api/v1/messages \
-H "Authorization: Bearer smc_your_token_here" \
-H "Content-Type: application/json" \
-d '{ "space_key": "OPS", "email": "person@example.com", "subject": "Update", "body": "Hello." }'If the address is new to the space, sending creates a contact for it. Every message carries an unsubscribe link.
Sending is rate-limited. By default a space may send up to 1,000 messages a day,
and a single lease up to 10 a minute; an instance can tune these. Over the cap, a
send is refused with 429 and rate_limited. The message names the scope and its
limit — messages cap reached for lease (limit: 10) or, for the daily cap,
messages cap reached for space (limit: 1000).
Contacts
POST /api/v1/spaces/:space_key/contacts; MCP contacts_create_or_update; the
web interface. Needs member. The call is idempotent on the space and email:
the same address updates the existing contact.
A contact has first_name, last_name, language, timezone, and bio, and
two independent statuses:
mail_consent_status— what the person asked for:subscribedorunsubscribed. New contacts aresubscribed.mail_deliverability_status— what their mailbox does:unknown,deliverable, orundeliverable. New contacts areunknown.
Deliverability and unsubscribe
These two statuses behave differently, and the difference matters.
Deliverability is one-way-back. When a contact's mailbox marks a message
undeliverable, further sends are held. Once the address is fixed, an operator
clears the mark in the web interface — POST /spaces/:space_key/contacts/:id/clear_deliverability, which needs member — and
the next send is allowed again.
Consent withdrawal is permanent. Every message carries a link to GET /unsubscribe/:token, a public page that needs no sign-in — the signed token in
the link is the credential. Visiting it sets the contact's consent to
unsubscribed, and there is deliberately no route that re-subscribes them. The
page is idempotent: a second visit shows the same confirmation.
Slack notification rules
A notification rule posts to Slack when a matching activity happens. MCP
notification_rules_create, notification_rules_update, notification_rules_list,
notification_rules_show, notification_rules_delete; the web interface. Every
one needs admin.
A rule has:
trigger_kind— the activity kind that fires it.filter— an optional narrowing byworkflow,stage,task_type, andtags.workflow,stage, andtask_typeeach hold one record's numeric id, andtagsa list of tag ids. For where to find a workflow's id, see Workflows and stages § Workflows. A rule matches only when every key you set matches. Any other key is refused withFilter has unknown keys: <keys>. Deleting a workflow, stage, task type, or tag that a rule'sfilternames deletes the rule too, with no warning.target_type—channel(posts totarget_address, a Slack channel),dm_assignee(direct-messages the task's assignee), ordm_time_log_owner.sender_id— the Slack app persona that posts.title_template,message_template— optional Liquid templates for the post; without them, the kind's default wording is used. A template may usetask.key,task.title,task.url,task.stage,task.workflow(the workflow's slug),task.workflow_name,task.team,task.team_name,task.type,task.type_name,task.priority,task.assignee,task.hold_reason, andactor. Any other variable is refused when the rule is saved, with a message reading… references unknown variable '<name>'.
Rules have no enabled flag: deleting a rule is how you turn it off.
Slack apps
A Slack app is a persona a notification rule posts as — "post as Elara" means post
with the Elara app's bot credentials. You add and remove them in the web interface
only — POST /spaces/:space_key/slack_apps, DELETE .../slack_apps/:id — and both
need admin. An app carries a bot_token, a signing_secret, a bot_user_id,
and an operator_user_id. A space may hold several.