This val summarizes recent events on Attio lists on Slack.
This project receives Attio webhooks, verifies and processes them, and stores events, messages, and list entry state data in SQLite. A scheduled cron job uses then periodically sends summaries with these stored messages to Slack.
- Remix this val
- Get a Slack webhook
& set it as
SLACK_WEBHOOK_URL
in this val's Environment variables in the left sidebar - Get an Attio Access Token
(with all read & write permissions) & set it as
ATTIO_API_KEY
in this val's Environment variables in the left sidebar - Set
config.workspaceName
inconfig.ts
to the name of your Attio workspace - For each list you want to get notifications about, add an object to the
config.lists
array inconfig.ts
. This object must contain alistId
,notificationHeader
, andobjectName
.- To find your list's id, navigate to the list and copy the path segment
after
collection
:https://app.attio.com/<workspaceName/collection/<listId>
. - The
notificationHeader
is the header that will appear in slack. objectName
is the singular name of the object that list entries in the target list are a instances of. If you're unsure, navigate to the record for one of the list entries and copy the path segment after your workspace's name:https://app.attio.com/<workspaceName/<objectName>/<record_id>
- To find your list's id, navigate to the list and copy the path segment
after
- Go to
setup.ts
and click run to set up the events database and Attio webhook - Go to
alert.ts
and click run to set up the cron job - Go trigger some Attio events and see the message in Slack!
If you want to get notifications about more than one list from your workspace,
simply add another listConfig
object to the lists
array in
config.ts
the same way as you did in step 5, above.
Then, go to add-list.ts
and run the script. This will
initialize the list in SQLite.
To format custom Attio attribute types in your Slack messages, create a new formatter in the formatters/
directory:
// formatters/custom-formatter.ts
export function customFormatter(value: CustomAttributeValue): string {
return value.customField;
}
Then import and use it in your message building logic.
The Slack message formatting is handled in shared/slack.ts
. You can customize:
- Message headers and structure
- Link formatting
- Text truncation
- Message grouping logic
To handle additional Attio webhook event types:
- Add the event type to
types/webhook.ts
- Create a new event handler class in
services/webhook/handle-webhook.ts
- Register it in the
eventHandlers
array
If you need to modify the database schema:
- Update
database/schema.ts
with your new table/index definitions - Run the migration by executing
createTables()
in a script - Update the corresponding TypeScript types
The project uses these environment variables:
ATTIO_API_KEY
- Your Attio API access tokenSLACK_WEBHOOK_URL
- Your Slack webhook URL
The project includes retry logic for API calls and comprehensive error logging. You can customize retry behavior in shared/retry.ts
.
API responses are cached for 5 minutes to reduce redundant calls. Cache behavior can be modified in shared/cache.ts
.
This project is designed to run on Val Town. To deploy:
- Fork this val on Val Town
- Set environment variables in the Val Town interface:
ATTIO_API_KEY
- Your Attio API access tokenSLACK_WEBHOOK_URL
- Your Slack webhook URL
- Run setup scripts in order:
scripts/setup.ts
- Creates database and webhookscripts/bootstrap.ts
- Seeds initial data
- Set up the cron job by running
alert.ts
and scheduling it - Test the webhook by triggering some Attio events
Required environment variables:
ATTIO_API_KEY
- Attio API access token with read/write permissionsSLACK_WEBHOOK_URL
- Slack incoming webhook URL
The application provides health check endpoints:
GET /
- Basic health statusGET /health
- Detailed health information
Monitor these endpoints to ensure the service is running correctly.
Webhook not receiving events:
- Verify the webhook URL is correct in Attio
- Check that the webhook is enabled for the right event types
- Ensure the webhook secret is properly configured
Slack messages not appearing:
- Verify
SLACK_WEBHOOK_URL
is set correctly - Check Slack webhook permissions
- Review logs for error messages
Database issues:
- Run
scripts/setup.ts
to recreate tables - Check Val Town SQLite storage limits
- Verify database indexes are created properly
attio-slack-summaries/
├── webhook.ts # Main HTTP endpoint - receives Attio webhooks
├── alert.ts # Cron job - processes recent events & sends Slack summaries
├── services/
│ ├── alert/
│ │ └── process-alerts.ts # Alert processing logic
│ ├── attio/
│ │ ├── webhook.ts # Attio webhook management - creates/stores webhook configs
│ │ └── utils.ts # Attio API utilities & helpers
│ └── webhook/
│ └── handle-webhook.ts # Webhook event processing logic
├── shared/
│ ├── slack.ts # Slack integration - message formatting & webhook sending
│ └── enrich-state.ts # Data enrichment for CRM entries
├── types/
│ ├── index.ts # Core application types
│ ├── webhook.ts # Attio webhook payload type definitions
│ └── attribute-values.ts # Attribute value type definitions
├── database/ # Data persistence layer
│ ├── schema.ts # SQLite table definitions (events, messages, entries)
│ └── sqlite.ts # Database operations - CRUD for webhook events & messages
└── scripts/
├── setup.ts # One-time setup - creates DB tables & Attio webhook
└── bootstrap.ts # Bootstrap script for initial setup
Attio webhooks return thin events, which contain a bunch of IDs, but no human-readable data, such as:
{ "event_type": "record.merged", "id": { "workspace_id": "b6c564a6-2cf7-49ab-9320-dea013196bd7", "object_id": "a87cf74e-5ca1-4a8d-b8d3-fcca5413d4c3", "record_id": "d64ff9f2-d1f1-424c-8be6-e41129e35697" }, "duplicate_object_id": "a87cf74e-5ca1-4a8d-b8d3-fcca5413d4c3", "duplicate_record_id": "112b5c78-1ffe-457a-8366-181b482888b4", "actor": { "type": "system", "id": null } }
The trickiest part of this val was transformning these events into data that could be used to track the state of entries in lists. I've accomplished this here by storing the state of each entry in a SQLite database and then comparing subsequent states to the previous state of the entry to determine the changes.
These state objects are then used to generate message objects that are also stored in the SQLite database. When the cron job runs, it takes these message objects formats them into a nice payload and sends them to Slack.