Skip to content

A chat app (a bot) is a member of Repzo Chat that your code drives. It posts messages built from Block Kit blocks, hears when people click its buttons or fill in its forms, opens modals, and can give each person a Home tab.

If you have built a Slack app, you already know most of it: the payloads, method names (chat.postMessage, views.open, …) and interaction shapes are Slack’s. The differences are listed on each reference page under How Repzo Chat differs from Slack.

  1. Open Settings → Integrations & Developers → Developer Apps and choose the Bots tab.
  2. Create a bot. Give it a name and avatar — that’s how it appears in Chat.
  3. Copy its foxa-* token when it is shown. Workstation stores only a hash and can’t show it again.
  4. Under Interactivity, set the HTTPS URL that should receive clicks and form submissions. Copy the signing secret shown once — you need it to verify requests.
  5. Add the bot to the channels it should post in (public channels work without joining when the bot has chat:write.public).

Keep the token in a secret store or environment variable. Never commit it.

Slack-compatible bot API Public REST API v1
Base path /api/bot/<method> /api/v1/...
Style Slack method names and bodies, answers { "ok": true, ... } REST resources, OpenAPI-documented at /api/v1/docs
Best for Porting Slack apps, Slack SDKs pointed at Repzo New integrations that also use other Workstation resources

Both take a Authorization: Bearer foxa-… header and the same Block Kit JSON.

Method What it does
POST /api/bot/chat.postMessage Post a message (channel, text, blocks, attachments, thread_ts)
POST /api/bot/chat.update Edit a message your bot posted (ts, text, blocks, attachments)
POST /api/bot/chat.delete Delete a message your bot posted (ts)
POST /api/bot/chat.postEphemeral Post a message only one person sees
POST /api/bot/views.open / views.push / views.update Open, stack and update modals
POST /api/bot/views.publish Set a person’s Home tab
GET /api/bot/channels.list / channels.info List public channels / look one up
GET /api/bot/files/:id Download a file someone attached in your form (the link comes in the payload)

channel accepts a channel id or its short name (sales or #sales).

A bot’s scopes are set when it is created: chat:write.public (post in public channels), chat:write (post anywhere it’s a member), chat:write.thread, chat:update, chat:delete and channels:read.

Post a message with a button:

Terminal window
curl -X POST https://workstation.repzo.com/api/bot/chat.postMessage \
-H "Authorization: Bearer $REPZO_BOT_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"channel": "sales",
"text": "New deal: Acme expansion",
"blocks": [
{ "type": "section", "text": { "type": "mrkdwn", "text": "*New deal:* Acme expansion — $12,000" } },
{ "type": "actions", "elements": [
{ "type": "button", "action_id": "approve", "style": "primary", "text": { "type": "plain_text", "text": "Approve" }, "value": "deal-42" }
] }
]
}'

When someone clicks Approve, your interactivity URL receives a block_actions payload.

Every request Workstation sends your app — clicks, form submissions, option lookups, events — is signed with your signing secret:

X-Fox-Timestamp: 1791799555
X-Fox-Signature: sha256=5f2c…

The signature is HMAC-SHA256(secret, "<timestamp>.<raw request body>") as hex. Recompute it over the raw body, compare in constant time, and reject requests older than five minutes.

import crypto from "node:crypto";
function isFromRepzo(rawBody, headers, secret) {
const timestamp = headers["x-fox-timestamp"];
if (Math.abs(Date.now() / 1000 - Number(timestamp)) > 300) return false;
const expected = "sha256=" + crypto.createHmac("sha256", secret).update(`${timestamp}.${rawBody}`).digest("hex");
const given = headers["x-fox-signature"] ?? "";
return given.length === expected.length && crypto.timingSafeEqual(Buffer.from(given), Buffer.from(expected));
}

A sandbox has its own Developer Apps. Requests to your own bot endpoints are delivered from sandboxes too, so you can test an app end to end without touching the live workspace.