Automatically creates HubSpot tickets when threads are created in Plain.
-
Install dependencies:
bun install
-
Configure environment:
cp .env.example .env # Edit .env with your API keys -
Run the server:
bun run index.ts
-
Set up webhook in Plain:
- Go to Plain Settings → Webhooks
- Add webhook URL:
https://your-domain.com/plain/webhook - Subscribe to
thread.thread_createdevents
Copy .env.example to .env and fill in:
| Variable | Description | Where to get it |
|---|---|---|
PLAIN_API_KEY |
Plain API key | Plain Settings → API Keys |
HUBSPOT_ACCESS_TOKEN |
HubSpot access token | HubSpot Developer |
HUBSPOT_REFRESH_TOKEN |
HubSpot refresh token | HubSpot OAuth flow |
HUBSPOT_CLIENT_ID |
HubSpot app client ID | HubSpot Developer |
HUBSPOT_CLIENT_SECRET |
HubSpot app client secret | HubSpot Developer |
- Bun runtime
- Plain workspace with API access
- HubSpot account with API access
- HubSpot app with scopes:
tickets,crm.objects.companies.read
- Receives
thread.thread_createdwebhook from Plain - Fetches thread details to get tenant ID
- Fetches tenant details to get HubSpot company ID (
externalId) - Creates HubSpot ticket associated with the company
- Automatically refreshes HubSpot token if expired
Ticket Format:
- Subject: Thread title
- Content:
{title}\n\n{description}\n\nSee in Plain: {threadUrl} - Pipeline: 0, Stage: 1
Each Plain tenant must have its externalId set to the corresponding HubSpot company ID.
Example:
- Plain Tenant ID:
te_01K65K8CH509YRN5HYEEH9DJHP - Plain Tenant External ID:
244982408439 - HubSpot Company ID:
244982408439✓
GET /health- Health checkPOST /plain/webhook- Webhook receiver
MIT