Cadence
Reference

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: subscribed or unsubscribed. New contacts are subscribed.
  • mail_deliverability_status — what their mailbox does: unknown, deliverable, or undeliverable. New contacts are unknown.

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 by workflow, stage, task_type, and tags. workflow, stage, and task_type each hold one record's numeric id, and tags a 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 with Filter has unknown keys: <keys>. Deleting a workflow, stage, task type, or tag that a rule's filter names deletes the rule too, with no warning.
  • target_type — channel (posts to target_address, a Slack channel), dm_assignee (direct-messages the task's assignee), or dm_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 use task.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, and actor. 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.

On this page