@crvouga/mockingbird-service-klaviyo 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md ADDED
@@ -0,0 +1,5 @@
1
+ # Changelog — @crvouga/mockingbird-service-klaviyo
2
+
3
+ ## 0.1.0 (2026-09-22)
4
+
5
+ Initial release.
package/README.md ADDED
@@ -0,0 +1,93 @@
1
+ # @crvouga/mockingbird-service-klaviyo
2
+
3
+ Stateful mock of the **Klaviyo** events API for test suites: the JSON:API create-event endpoint
4
+ our backend posts `Ordered Product` and `Placed Order` to after checkout, the event reads, and an
5
+ outbox a suite asserts on. Errors come back in Klaviyo's JSON:API `errors[]` shape, so the text
6
+ our client throws (and logs) is the text the real API would produce.
7
+
8
+ - Operation coverage: [SUPPORT.md](https://github.com/crvouga/mockingbird/blob/main/packages/service/klaviyo/SUPPORT.md)
9
+ - The contract (`openapi.yaml`) is trimmed from Klaviyo's published API (revision `2024-02-15`,
10
+ the one our backend pins) to what our consumer sends.
11
+
12
+ ## Install
13
+
14
+ ```bash
15
+ npm install -D @crvouga/mockingbird-service-klaviyo
16
+ ```
17
+
18
+ ESM only. Node >= 22 or Bun >= 1.2. No native dependencies. Serve it with
19
+ `npx mockingbird-klaviyo serve`, `createServer` from `./server` (Node), or `createRuntime` with
20
+ any Fetch server.
21
+
22
+ ## Usage
23
+
24
+ Point `KLAVIYO_URL` (Joi-required, fully overridable) at the mock's event endpoint; any
25
+ `KLAVIYO_API_KEY` works.
26
+
27
+ ```bash
28
+ npx mockingbird-klaviyo serve --port 8811
29
+ # KLAVIYO_URL=http://127.0.0.1:8811/api/events/
30
+ ```
31
+
32
+ ```ts
33
+ import { createRuntime } from "@crvouga/mockingbird-service-klaviyo"
34
+
35
+ const klaviyo = createRuntime()
36
+ // …the app posts POST /api/events/ with Authorization: Klaviyo-API-Key <key>, revision: 2024-02-15…
37
+ const response = await klaviyo.fetch(
38
+ new Request("http://klaviyo.test/__admin/outbox?to=ada@example.com&metric=Placed%20Order"),
39
+ )
40
+ const { messages } = (await response.json()) as { messages: { metric: string; value: number }[] }
41
+ ```
42
+
43
+ ### Routes
44
+
45
+ | Route | Behaviour |
46
+ | --- | --- |
47
+ | `POST /api/events/` | JSON:API `{data: {type: "event", attributes: {properties, time?, value?, value_currency?, unique_id?, metric: {data: {type: "metric", attributes: {name}}}, profile: {data: {type: "profile", id?, attributes: {email?, phone_number?, external_id?}}}}}}` → **202, no body**. The profile is matched by id, email, phone or external id (else created); the metric is created on first use. A repeat of metric + profile + `unique_id` is accepted but not stored twice (a BullMQ retry does not double revenue). Invalid input is 400 `{errors: [{id, status, code: "invalid", title, detail, source: {pointer}}]}`; a phone number that is not E.164 is rejected with Klaviyo's wording. |
48
+ | `GET /api/events/` | `{data: [event], links: {self, next: null, prev: null}}`, oldest first. Each event carries `attributes.{timestamp, event_properties (properties + $value + $event_id), datetime, uuid}` and `relationships.{profile, metric}`. |
49
+ | `GET /api/events/{id}/` | One event, or 404 `not_found`. |
50
+
51
+ Every call needs `Authorization: Klaviyo-API-Key <key>` (else 401 `not_authenticated`) and a
52
+ `revision: YYYY-MM-DD` header (else 400). Paths answer with or without the trailing slash.
53
+
54
+ ### Admin (beyond the standard contract)
55
+
56
+ | Route | Effect |
57
+ | --- | --- |
58
+ | `GET /__admin/outbox?to=&since=&metric=&unique_id=` | Accepted events of the calling namespace, oldest first (`to` matches the profile's email, phone or id). `GET /__admin/outbox/:id` for one. |
59
+ | `GET /__admin/profiles` | Profiles created or matched by events. |
60
+
61
+ Fault presets (`POST /__admin/faults {"preset": "<name>", "count"?: n}`; `GET /__admin/faults/presets`):
62
+ `throttled` (429 + `Retry-After: 1`), `invalid_api_key` (401), `server_error` (500),
63
+ `service_unavailable` (503), `connection_drop` (the socket closes; nothing is stored).
64
+
65
+ ### Namespaces
66
+
67
+ `x-mockingbird-namespace`, a `/ns/<name>` prefix in `KLAVIYO_URL`, or by private key:
68
+ `PUT /__admin/credentials {"credentials": {"<KLAVIYO_API_KEY>": "<namespace>"}}`.
69
+
70
+ ### Deliberately not modelled
71
+
72
+ - Flows, lists, segments and campaigns: an event never triggers an email (use the outbox).
73
+ - Profile-id validation: Klaviyo profile ids are Klaviyo-generated; our backend sends the user
74
+ token as `profile.data.id`, which the mock adopts as the profile id rather than rejecting.
75
+ Whether the real API accepts an unknown id is unverified (no sandbox credentials).
76
+ - Pagination (`page[cursor]`), `filter`, `fields[…]` and `include` on reads.
77
+ - Rate limits, except through the `throttled` preset.
78
+
79
+ ## API
80
+
81
+ | Export | Kind | Description |
82
+ | --- | --- | --- |
83
+ | `KlaviyoAPI` | class | The in-process mock: `fetch(request)`, `reset()`, `events()`, `state`. Options: `sqlite`, `now`, `namespace`, `baseUrl`. |
84
+ | `createRuntime` | function | The mock with the full service contract (health, admin, namespaces, credentials, presets, outbox). Options: `baseUrl`, `clock`, `seed`, `adminKey`, `onLog`, `sqlite`. |
85
+ | `KLAVIYO_PRESETS` | object | Every named fault preset. |
86
+ | `KLAVIYO_NAMESPACE` | string | The service name, `"klaviyo"`. |
87
+ | `KLAVIYO_REVISION` | string | The API revision our backend sends, `"2024-02-15"`. |
88
+ | `klaviyoApiKey` | function | The key in `Authorization: Klaviyo-API-Key <key>` (how credentials map to namespaces). |
89
+ | `klaviyoError` | function | Build a JSON:API `{errors: [...]}` body. |
90
+ | `document`, `operationIds`, `supportedOperationIds` | values | The vendored OpenAPI contract and its operation ids. |
91
+ | `createServer`, `serveTarget`, `DEFAULT_PORT` (`./server`) | Node | Serve over `node:http`; the `serve` CLI target; port 8811. |
92
+
93
+ Part of [mockingbird](https://github.com/crvouga/mockingbird).