Event catalog
Kudos CRM accepts events from this catalog only. A POST with an event_type outside the list returns 422 Unprocessable Entity with code: "event_type_not_in_catalog" — the request is rejected before any tenant data is touched.
This is deliberate. A closed catalog gives both sides a stable contract to test against and prevents one-off payload shapes from accumulating in production.
The 36 types
| Event type | Fires when | Maps to CRM |
|---|---|---|
signup.requested | Visitor submits the sign-up form | Lead created |
signup.email_verified | OTP / email click confirms | Lead enriched |
signup.qualified | Producer-side qualification logic passes | Lead → MQL |
signup.approved | Operator approves the new tenant | Lead → SQL, Deal opens |
signup.rejected | Operator rejects the sign-up | Lead → unqualified |
What you see above is the default mapping each event triggers. The receiving organization can customize how any event maps to its CRM — which object it upserts, which fields and tags it sets, the pipeline stage and deal economics — per connection, with no code. The set of event types the CRM recognizes out of the box is always available, machine-readable, from the live manifest endpoint:
curl https://api.kudoscrm.com/api/v1/ingest/manifest
# → { "consumer": "crm", "supported_schema_versions": [1], "supported_event_types": [ … ] }
Treat the manifest as the source of truth for the accepted catalog; this page is the human-readable companion.
Envelope
Every event uses the same outer envelope. The data field carries the per-type payload.
{
"schema_version": 1,
"event_id": "evt_01HZQK7C8R0T8Z2W5N4Y9P3X6V",
"event_type": "subscription.upgraded",
"fired_at": "2026-05-30T08:42:11Z",
"producer": { "name": "kudos", "version": "2.4.0" },
"tenant": {
"external_ref": "kudos_tenant_42",
"name": "Kudos Corp",
"country": "US"
},
"data": { "//": "per-type payload" }
}
| Field | Type | Required | Notes |
|---|---|---|---|
schema_version | integer | yes | Major envelope version. Current is 1. |
event_id | string | yes | UUIDv7 with evt_ prefix, 25-char base32 body. Idempotency key. |
event_type | string | yes | Must be in the catalog above. |
fired_at | string | yes | RFC 3339 UTC. Wall-clock on the producer at emit time. |
producer.name | string | yes | Stable identifier of your product. |
producer.version | string | yes | Your release version. Aids debugging. |
tenant.external_ref | string | usually | Producer-side tenant id. Omit only for cross-tenant events (e.g. webhook.ping). |
tenant.name / tenant.country | string | no | Convenience for the CRM mapper. |
data | object | yes | Per-type payload. Shape is documented per event in the API reference. |
Per-type payload examples
signup.requested
{
"data": {
"lead_first_name": "Alex",
"lead_last_name": "Morgan",
"company_name": "Kudos Corp",
"country": "US",
"lead_source": "google_ads",
"utm": { "source": "google", "medium": "cpc", "campaign": "spring_2026" }
}
}
subscription.created
{
"data": {
"subscription_id": "sub_01HZQK7C8R0T8Z2W5N4Y9P3X6V",
"plan_code": "growth_yearly",
"tier": "growth",
"billing_cycle": "yearly",
"currency": "USD",
"mrr_minor": 49900,
"arr_minor": 598800,
"seats": 25,
"starts_at": "2026-05-30T00:00:00Z",
"ends_at": "2027-05-30T00:00:00Z",
"trial": null
}
}
subscription.upgraded
{
"data": {
"subscription_id": "sub_01HZQK…",
"from": { "plan_code": "starter_monthly", "tier": "starter", "mrr_minor": 9900 },
"to": { "plan_code": "growth_yearly", "tier": "growth", "mrr_minor": 49900 },
"delta_mrr_minor": 40000,
"delta_arr_minor": 480000,
"effective_at": "2026-05-30T08:42:11Z"
}
}
payment.succeeded
{
"data": {
"payment_id": "pay_01HZQK…",
"subscription_id":"sub_01HZQK…",
"invoice_id": "inv_01HZQK…",
"amount_minor": 49900,
"currency": "USD",
"method": "card",
"captured_at": "2026-05-30T08:42:11Z"
}
}
tenant.suspended
{
"data": {
"reason_code": "non_payment",
"reason": "Three consecutive failed payments",
"suspended_at": "2026-05-30T08:42:11Z",
"actor": { "kind": "operator", "id": "op_…" },
"retention_until": "2026-08-28T00:00:00Z"
}
}
webhook.ping
{
"data": {
"source": "manual_test_fire",
}
}
Tenant is null on ping events.
The full payload reference for every event is in the API Reference tab (try-it playground per endpoint).
Adding new event types
We add new event types via the additive rule: a producer that does not know about a new type continues working unchanged. Consumers that do not handle a new type ignore it gracefully. Major version bumps are reserved for breaking changes (renames, removed fields, type changes), and trigger the 180-day deprecation window described on the introduction page.
If you need an event type that is not in the catalog, email [email protected] — we triage every request and tend to land additions within one sprint.