Events in, not just out
Connect Stripe, your email provider, your CRM, your automations and your LLM spend — and get one stream of verified, normalised events through the same pipeline that already delivers your webhooks. Same retries, same timeline, same replay.
In short
- A source brings events in; an endpoint sends them out. Both share one pipeline.
- Push sources (Stripe, SendGrid, GoHighLevel, n8n, Postmark) are verified at the door — signature over the raw bytes, before anything is stored.
- Pull sources (OpenAI, Anthropic) are read on a schedule with a cursor: token usage and cost per model become events.
- Connectors normalise:
payment.succeededwithamount_cents, not a vendor-flavoured blob. Unmapped events pass through rather than being dropped. - Existing sources keep their old event names — canonical naming applies only to sources created from now on, so no filter breaks.
The catalogue
| Provider | Key | Mode | Verified by | What it produces |
|---|---|---|---|---|
| Stripe | stripe | Push | HMAC signature | payment, refund, invoice, subscription, dispute |
| GoHighLevel | gohighlevel | Push | Ed25519 / RSA | lead, deal, appointment |
| SendGrid | sendgrid | Push | ECDSA P-256 | email delivered, opened, clicked, bounced |
| Postmark | postmark | Push | Shared token | email delivered, bounced, complained |
| n8n | n8n | Push | Shared token | workflow run succeeded / failed |
| Twilio | twilio | Push | HMAC signature | inbound calls, missed calls, SMS |
| Meta Lead Ads | meta-leads | Push | HMAC signature | leads from Facebook & Instagram forms |
| OpenAI | openai | Pull | Admin key | tokens and cost per model, hourly |
| Anthropic | anthropic | Pull | Admin key | Claude tokens and cost per model, hourly |
| Google Analytics 4 | google-analytics | Pull | Service account | daily users, sessions, page views |
| Search Console | google-search-console | Pull | Service account | daily clicks, impressions, position |
| Meta Ads | meta-ads | Pull | Access token | daily spend, impressions, clicks, reach |
| TikTok Ads | tiktok-ads | Pull | Access token | daily spend, impressions, clicks, reach |
Alongside these, 15 inbound providers — GitHub, Shopify, Standard Webhooks, Alibaba EventBridge and more — are verified at the door and passed through under their own topic names. Verification always happens before an event exists.
One envelope, whatever the provider
Each connector extracts the same canonical shape, stored beside your payload — never inside it, so the bytes a signature covers and the body your consumers receive are untouched.
"eventType": "payment.succeeded"
Canonical, not vendor-flavoured — one alert covers every payment provider"occurredAt": "2026-08-20T18:20:00.000Z"
When it happened at the source. receivedAt is kept separately — provider delays stay visible"entity": {"type":"customer","id":"cus_9"}
Who or what it is about. Identifiers live here, never in attributes"attributes": {"provider":"stripe","currency":"usd"}
Only things you would group by. Ids, emails and URLs are refused by name"metrics": {"amount_cents":4299}
Finite numbers only, unit in the name. All money in cents, whatever the provider reports"privacy": {"classification":"confidential","containsPii":true}
Declared per event. The default is the strictest class, on purposeAll money is in cents. Stripe reports cents, GoHighLevel reports whole
currency units, Anthropic reports cents as a decimal string. Each connector converts at its own
boundary, so amount_cents means one thing everywhere — a cross-source number you
cannot trust is worse than no number.
Connect one in three steps
| Step | Push source | Pull source |
|---|---|---|
| 1 | Create the source — you get an ingest URL and a secret | Create the source with the provider credential |
| 2 | Paste the secret into the provider's webhook settings | Choose the interval (default 5 minutes) and how far back to backfill (up to 90 days) |
| 3 | Events arrive, verified and normalised | The first sync runs immediately; health is reported per source |
curl -X POST https://api.hookget.com/v1/sources \
-H "authorization: Bearer $HOOKGET_KEY" \
-H "content-type: application/json" \
-d '{"provider":"stripe","name":"prod payments"}'
# {"id":"src_…","secret":"whsec_…","ingest_path":"/ingest/src_…","mode":"push"}
Stated plainly
| Fact | Why it is this way |
|---|---|
| Pull syncs skip the most recent ~90 minutes | Providers keep writing into the current bucket; reading it early would double-count the same hour on the next sync. |
| A failed sync keeps its old cursor | A cursor advanced mid-failure silently skips events. Re-reads are free — the provider’s own delivery id deduplicates. |
| A source failing 20 syncs in a row pauses itself | Retrying a revoked key every five minutes, forever, gets us rate-limited by the provider in your name. |
| Postmark and n8n are token-verified, not signed | Neither offers payload signing. We state the strongest verification that exists rather than implying one that does not. |
| GoHighLevel verification proves the sender, not the account | GHL signs with one key pair for all its customers. Filter on locationId if the distinction matters to you. |
Questions
What is an event source?
The inverse of an endpoint. An endpoint is somewhere HookGet delivers to; a source is somewhere HookGet gets events from — a SaaS that sends webhooks, or an API we read on a schedule. Once an event is in, it takes exactly the same path as one you publish yourself: the same fan-out, signatures, retries, dead-letter queue and metering.
How is this different from the 15 inbound providers you already had?
Those verify a provider's signature and pass the body through under the provider's own
topic name — github.push, shopify.orders.create. They still work,
unchanged. The seven connectors on this page additionally normalise: a Stripe
payment_intent.succeeded becomes payment.succeeded with the amount
as a metric in cents and the customer as an entity, so one filter or one alert covers every
payment provider you connect.
Will canonical names break my existing filters?
No. A source created before connectors existed keeps emitting the provider-flavoured
names your filters already match — that behaviour is pinned per source, in the database, not
in your configuration. Only sources created from now on default to canonical names, and you
can choose either at creation with event_naming.
What happens to an event type you have not mapped?
It keeps the provider's name and flows through anyway. Stripe alone has several hundred
event types; the mapped set covers what a revenue pipeline needs, and everything else arrives
as stripe.<their name> rather than being dropped. Losing your
charge.dispute.created because we had not mapped it yet is a failure mode this
design refuses.
Where do the events go?
Anywhere HookGet delivers: HTTPS endpoints, queues and buses (SQS, EventBridge, Kafka,
RabbitMQ, Pub/Sub), S3-compatible storage, or a polling cursor. Filters and transformations
apply per destination, so the same payment.succeeded can hit your warehouse in
full and your Slack alert as three fields.