Build an app
An app is a program on your own server that posts to buildIt.social channels, hears what happens in them, and sends interactive cards whose answers come back to it: a deploy bot, a help desk that answers when mentioned, a stand-up form, an alert people acknowledge. This page is for the developer who builds it.
How it fits together#
- An org owner or admin creates the app in buildIt.social, picks its permissions and events, and gives you its token and signing secret, each shown once. See Create an app.
- Your app calls the app API over HTTPS with its token.
- It hears about events through signed webhooks to a public HTTPS URL, or by polling if it has no public URL. See Events and webhooks.
- Someone adds the app to a channel. From then on it can post there, and gets events from there.
What an app is#
- Its own identity, not a person. It has a name, a description and a rounded-square picture, and shows an APP badge everywhere. It isn’t a member of the organization and doesn’t take a seat.
- Made by the org, for the org. Only org owners and admins create and manage apps, with a two-factor sign-in. An organization has at most 10 apps.
- In a channel only when someone adds it. It sees no channel by default, and adding or removing it writes a line in the channel saying who did it.
What an app can do#
- Read the channels it’s in, with their members’ names (never emails).
- With Read messages, read those channels’ history, including messages from before it was added, and get message events. With only Messages that mention it, get just the messages that mention it.
- Post, edit and delete its own messages (Markdown, up to 4,000 characters), at the top level or in a thread, with or without a card; mention people in the channel; react to messages; pin and unpin messages.
- Send cards, read their answers, accept or reject an answer with a note only that person sees, update a card for everyone, and close it.
- Hear about members joining and leaving, reactions, pins, calls starting and ending, and work items created, changed in status or commented on in the projects of its channels.
- Read a work item by its key, in a project whose channel it’s in.
What an app can’t do#
- No direct messages. An app can’t be added to a DM or group DM, gets no events from one, and the API answers
404for any DM’s id. - No files or images. It can’t upload or read attachments (a message only says
has_attachment), and Markdown images are refused. - No slash commands, dialogs or “only you can see this” messages. Mentions are how people talk to an app; a note on someone’s card answer is the private reply.
- No changes to work items and no starting calls.
- No notifying the whole channel. Mentions notify people under their own settings.
- No styling. Cards take no colours, fonts or images, and an app can’t change its name or picture per message.
- No sharing across organizations. Each app belongs to one organization.
Authentication#
Every request sends the app’s token as a bearer token:
Authorization: Bearer buildit_app_EXAMPLE…
Nothing else: no cookies and no API key header. A token is buildit_app_ followed by 49 letters and digits. Personal access tokens (buildit_pat_…, which act as a person) are refused by the app API, and app tokens are refused by the agent API.
Keep the token and the signing secret (buildit_whsec_ and 64 hex digits) in your server’s environment or secret store, never in code, a repository, a browser or a mobile app, a log, a chat or a ticket. The API sends no CORS headers, so it can’t be called from a web page anyway.
export BUILDIT_APP_TOKEN='buildit_app_EXAMPLE_REPLACE_ME'
export BUILDIT_SIGNING_SECRET='buildit_whsec_EXAMPLE_REPLACE_ME'
export BUILDIT_API_BASE='https://api.buildit.social/functions/v1/app-api/v1'
Check that the token works:
curl -sS "$BUILDIT_API_BASE/me" -H "Authorization: Bearer $BUILDIT_APP_TOKEN"
The answer names the app, the organization, its permissions and events, how it gets events, the channels it’s in, and the rate limits in force.
The base URL and the rules#
-
Base URL:
https://api.buildit.social/functions/v1/app-api/v1. Paths on these pages start at/v1, soGET /v1/meishttps://api.buildit.social/functions/v1/app-api/v1/me. -
JSON in and out, UTF-8, with
Content-Type: application/jsonon every request that has a body. Bodies are at most 64 KB. Times are RFC 3339 in UTC with milliseconds, such as2026-10-20T15:02:11.482Z. -
From servers only. Never call the API from a browser or a mobile app, where the token would be exposed.
-
X-Request-Id: every response carries one. Send your own (1 to 128 ofA-Z a-z 0-9 . _ : -) to have it echoed, and quote it when you report a problem. -
/v1only grows. New endpoints, optional request fields, response fields, enum values, event types and error codes can appear at any time. Ignore fields you don’t know, and treat an unknown error code by its HTTP status.GET /v1/metalists any deprecations.
Permissions#
The admin picks the app’s permissions, called scopes in the API. Ask for the fewest your app needs.
| In the app | Scope | Allows |
|---|---|---|
| Its channels | channels:read |
List the channels the app is in, with their names, topics and members. Always granted. |
| Read messages | messages:read |
Read those channels’ history and threads, from before the app was added too; get message.* events, and reactions and pins on any message. |
| Messages that mention it | mentions:read |
Get only the messages that mention the app (app.mentioned). The scope for a command bot. |
| Post | messages:write |
Post, change and delete the app’s own messages, with or without a card; react; pin and unpin; close its cards and accept or reject their answers; get card.response, card.closed, and reactions and pins on its own messages. |
| Calls | calls:read |
Get call.started and call.ended in its channels. |
| Work items | projects:read |
Read work items by key in the projects of its channels, and get work_item.* events. |
A call without the scope it needs answers 403 scope_missing. When an admin gives the app messages:read after it was added to a private channel, that channel waits until one of its members allows it; until then, reading it answers 403 scope_not_confirmed, and its message events aren’t sent.
Tip: A command bot (“@Deploybot ship web”) needs only mentions:read and messages:write. Prefer that to messages:read, which reads every message in its channels, earlier ones included.
Keep it safe#
- Verify every webhook’s signature before acting on it, check the timestamp, and compare in constant time. See Verifying signatures.
- Deduplicate on the event’s
id: delivery is at least once. - Treat everything people write as untrusted input: message text, names and form answers. Don’t run it as code, shell commands, or instructions to an AI model without care.
- Never put a secret in a card. Everything in a card except its
privatedata reaches every device in the channel. Never ask people for passwords or sign-in codes in a card. - Use one app per job, each with its own token, rather than one app with every scope.
Next#
- The app API: every endpoint.
- Events and webhooks: event types, signatures and retries.
- The card format: buttons, forms and answers.
- Example: a lunch bot in Node.