• Townie
    AI
  • Blog
  • Docs
  • Pricing
Log inSign up
nbbaier

nbbaier

attio-slack-summaries

Remix of stevekrouse/attio-slack-summaries
Public
Like
attio-slack-summaries
Home
Code
10
core
6
scripts
2
.vtignore
README.md
C
alert.ts
formatters.ts
slack-friendly.ts
slack.ts
types.ts
H
webhook.ts
Branches
4
Pull requests
Remixes
1
History
Environment variables
2
Val Town is a collaborative website to build and scale JavaScript apps.
Deploy APIs, crons, & store data – all from the browser, and deployed in milliseconds.
Sign up now
Code
/
README.md
Code
/
README.md
Search
9/7/2025
Viewing readonly version of simplification branch: v416
View latest version
README.md

Attio Slack Summaries

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.

Set up

  1. Remix this val
  2. Get a Slack webhook & set it as SLACK_WEBHOOK_URL in this val's Environment variables in the left sidebar
  3. 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
  4. Configure your lists in webhook.ts:
    • Update the listIds array with your Attio list IDs
    • To find your list's id, navigate to the list and copy the path segment after collection: https://app.attio.com/<workspaceName>/collection/<listId>
  5. (Optional) Customize attribute extractors in webhook.ts:
    • Modify the customExtractors object to customize how attribute values are formatted in Slack messages
    • See the "Customizing Attribute Extractors" section below for details
  6. Go to setup.ts and click run to set up the events database and Attio webhook
  7. Go to alert.ts and click run to set up the cron job
  8. Go trigger some Attio events and see the message in Slack!

Adding a list

If you want to get notifications about more than one list from your workspace, simply add the list ID to the listIds array in webhook.ts.

Then, go to bootstrap.ts and run the script. This will initialize the new list in SQLite.

Customization & Extension

Customizing Attribute Extractors

This template provides a flexible system for customizing how Attio attribute values are formatted in Slack messages. You can override any of the built-in extractors or add custom formatting.

Quick Start - Using the Webhook File

The easiest way to customize extractors is directly in the webhook.ts file:

// webhook.ts export const customExtractors: CustomExtractors = { // Add emojis to text values text: (value) => `✨ ${value.value} ✨`, // Format status with custom styling status: (value) => `šŸ”ø ${value.status.title}`, // Format currency with custom symbols currency: (value) => `šŸ’° ${Intl.NumberFormat("en-US", { style: "currency", currency: value.currency_code, }).format(Number(value.currency_value))}`, // Format dates with custom format date: (value) => new Date(value.value).toLocaleDateString("en-US", { year: "numeric", month: "long", day: "numeric", }), };

Available Attribute Types

You can customize any of these Attio attribute types:

Common Types:

  • text - Simple text values
  • status - Status values with titles
  • select - Dropdown/select values with option titles
  • date - Date values
  • timestamp - Timestamp values
  • rating - Numeric rating values
  • checkbox - Boolean checkbox values
  • currency - Currency values with proper formatting
  • record-reference - References to other records (fetches record name)
  • actor-reference - References to users/actors (fetches actor name)

Uncommon Types:

  • domain - Domain values
  • email-address - Email address values
  • interaction - Interaction values with type and timestamp
  • location - Location values with address formatting
  • number - Numeric values
  • personal-name - Personal name values
  • phone-number - Phone number values

Advanced Customization

For more complex customizations, you can:

  1. Override extractors programmatically:

    import { overrideExtractors } from "./extractors/index.ts"; overrideExtractors({ text: (value) => `✨ ${value.value} ✨`, });
  2. Pass custom extractors to functions:

    import { handleWebhookEvent } from "./services/webhook-handler.ts"; const customExtractors = { text: (value) => `Custom: ${value.value}`, }; await handleWebhookEvent(event, listIds, customExtractors);
  3. Use the bootstrap function with custom extractors:

    import { initialize } from "./scripts/bootstrap.ts"; await initialize(customExtractors);

Template Examples

Here are some common customization patterns you can use as templates:

Basic Text Formatting

export const customExtractors: CustomExtractors = { text: (value) => `šŸ“ ${value.value}`, status: (value) => `šŸ”ø ${value.status.title}`, select: (value) => `šŸ“‹ ${value.option.title}`, };

Currency and Number Formatting

export const customExtractors: CustomExtractors = { currency: (value) => `šŸ’° $${value.currency_value.toLocaleString()}`, number: (value) => `šŸ”¢ ${value.value.toLocaleString()}`, rating: (value) => `⭐ ${value.value}/5`, };

Date and Time Formatting

export const customExtractors: CustomExtractors = { date: (value) => `šŸ“… ${new Date(value.value).toLocaleDateString()}`, timestamp: (value) => `ā° ${new Date(value.value).toLocaleString()}`, };

Boolean and Checkbox Formatting

export const customExtractors: CustomExtractors = { checkbox: (value) => (value.value ? "āœ… Yes" : "āŒ No"), };

Contact Information Formatting

export const customExtractors: CustomExtractors = { "email-address": (value) => `šŸ“§ ${value.email_address}`, "phone-number": (value) => `šŸ“ž ${value.original_phone_number}`, "personal-name": (value) => `šŸ‘¤ ${value.full_name}`, };

Location Formatting

export const customExtractors: CustomExtractors = { location: (value) => { const parts = [ value.line_1, value.locality, value.region, value.country_code, ].filter(Boolean); return `šŸ“ ${parts.join(", ")}`; }, domain: (value) => `🌐 ${value.domain}`, };

Advanced Custom Formatting

export const customExtractors: CustomExtractors = { // Custom currency formatting with different symbols currency: (value) => { const symbol = value.currency_code === "USD" ? "$" : value.currency_code === "EUR" ? "€" : value.currency_code === "GBP" ? "Ā£" : value.currency_code; return `${symbol}${Number(value.currency_value).toLocaleString()}`; }, // Custom date formatting with relative time date: (value) => { const date = new Date(value.value); const now = new Date(); const diffInDays = Math.floor( (now.getTime() - date.getTime()) / (1000 * 60 * 60 * 24) ); if (diffInDays === 0) return `šŸ“… Today`; if (diffInDays === 1) return `šŸ“… Yesterday`; if (diffInDays < 7) return `šŸ“… ${diffInDays} days ago`; return `šŸ“… ${date.toLocaleDateString()}`; }, // Custom status formatting with colors status: (value) => { const status = value.status.title.toLowerCase(); if (status.includes("active") || status.includes("open")) return `🟢 ${value.status.title}`; if (status.includes("pending") || status.includes("waiting")) return `🟔 ${value.status.title}`; if (status.includes("closed") || status.includes("completed")) return `šŸ”“ ${value.status.title}`; return `⚪ ${value.status.title}`; }, };

How to Use Template Examples

  1. Copy any of the examples above
  2. Paste them into your webhook.ts file, replacing the empty customExtractors object
  3. Uncomment and modify the extractors you want to customize
  4. Save the file and your customizations will be applied automatically

Adding Custom Attribute Formatters

To format custom Attio attribute types in your Slack messages, you can add them to the customExtractors object in webhook.ts:

// webhook.ts export const customExtractors: CustomExtractors = { // Add custom formatters for any attribute type "custom-type": (value) => value.customField, };

The extractor system will automatically handle your custom formatters.

Customizing Message Format

The Slack message formatting is handled in shared/slack.ts. You can customize:

  • Message headers and structure
  • Link formatting
  • Text truncation
  • Message grouping logic

Adding New Event Types

To handle additional Attio webhook event types:

  1. Add the event type to shared/types.ts
  2. Update the webhook handler logic in services/webhook-handler.ts
  3. Add any new processing logic as needed

Database Schema Changes

If you need to modify the database schema:

  1. Update the table definitions in services/sqlite.ts
  2. Run the migration by executing createTables() in a script
  3. Update the corresponding TypeScript types in shared/types.ts

Environment Variables

The project uses these environment variables:

  • ATTIO_API_KEY - Your Attio API access token
  • SLACK_WEBHOOK_URL - Your Slack webhook URL

Error Handling

The project includes comprehensive error logging and graceful error handling throughout the webhook processing pipeline.

Deployment

Val Town Deployment

This project is designed to run on Val Town. To deploy:

  1. Fork this val on Val Town
  2. Set environment variables in the Val Town interface:
    • ATTIO_API_KEY - Your Attio API access token
    • SLACK_WEBHOOK_URL - Your Slack webhook URL
  3. Run setup scripts in order:
    • scripts/setup.ts - Creates database and webhook
    • scripts/bootstrap.ts - Seeds initial data
  4. Set up the cron job by running alert.ts and scheduling it
  5. Test the webhook by triggering some Attio events

Environment Variables

Required environment variables:

  • ATTIO_API_KEY - Attio API access token with read/write permissions
  • SLACK_WEBHOOK_URL - Slack incoming webhook URL

Health Monitoring

The application provides health check endpoints:

  • GET / - Basic health status
  • GET /health - Detailed health information

Monitor these endpoints to ensure the service is running correctly.

Troubleshooting

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

Project Structure

attio-slack-summaries/
ā”œā”€ā”€ webhook.ts                   # Main HTTP endpoint - receives Attio webhooks
ā”œā”€ā”€ alert.ts                     # Cron job - processes recent events & sends Slack summaries
ā”œā”€ā”€ services/
│   ā”œā”€ā”€ alert-processor.ts       # Alert processing logic
│   ā”œā”€ā”€ api-client.ts            # Attio API client utilities
│   ā”œā”€ā”€ notifications.ts         # Notification handling
│   ā”œā”€ā”€ sqlite.ts                # Database operations - CRUD for webhook events & messages
│   ā”œā”€ā”€ webhook-auth.ts          # Attio webhook management - creates/stores webhook configs
│   └── webhook-handler.ts       # Webhook event processing logic
ā”œā”€ā”€ shared/
│   ā”œā”€ā”€ slack.ts                 # Slack integration - message formatting & webhook sending
│   ā”œā”€ā”€ enrich-state.ts          # Data enrichment for CRM entries
│   └── types.ts                 # Core application types and type definitions
ā”œā”€ā”€ extractors/
│   ā”œā”€ā”€ index.ts                 # Attribute value extractors for all Attio types
│   └── README.md                # Documentation for the extractor system
ā”œā”€ā”€ scripts/
│   ā”œā”€ā”€ setup.ts                 # One-time setup - creates DB tables & Attio webhook
│   └── bootstrap.ts             # Bootstrap script for initial setup
└── dev-helpers/
    └── scratch.ts               # Development utilities and testing helpers

Attio webhooks

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.

FeaturesVersion controlCode intelligenceCLI
Use cases
TeamsAI agentsSlackGTM
ExploreDocsShowcaseTemplatesNewestTrendingAPI examplesNPM packages
PricingNewsletterBlogAboutCareersBrandhi@val.townStatus
X (Twitter)
Discord community
GitHub discussions
YouTube channel
Bluesky
Terms of usePrivacy policyAbuse contact
Ā© 2025 Val Town, Inc.