@crvouga/mockingbird-service-firstpromoter 3.12.1 → 3.14.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 CHANGED
@@ -1,5 +1,17 @@
1
1
  # Changelog — @crvouga/mockingbird-service-firstpromoter
2
2
 
3
+ ## 3.14.0 (2026-11-14)
4
+
5
+ ### Features
6
+
7
+ - add customerio, firstpromoter and healthie routes from agent reports ([6873133](https://github.com/crvouga/mockingbird/commit/68731332f83a8a347d2cafc17b8f9fed9b6a492a))
8
+
9
+ ## 3.13.0 (2026-11-12)
10
+
11
+ ### Features
12
+
13
+ - record delivered messages and show them in notifications ([b280c32](https://github.com/crvouga/mockingbird/commit/b280c32ec3e7f22f2c354747ab5193067290c26b))
14
+
3
15
  ## 3.12.1 (2026-11-12)
4
16
 
5
17
  ### Fixes and improvements
package/README.md CHANGED
@@ -5,7 +5,9 @@
5
5
  Stateful emulator of the **FirstPromoter v2** affiliate API for test suites: promoter create (adopt
6
6
  before create by `cust_id`), lookups by id / `cust_id` / `ref_token` / email, list, update,
7
7
  archive, the dashboard iframe login, signup tracking by click `tid`, `promoter_id` or ref token,
8
- and the Basic-auth `lead_becomes_referral` webhook our backend receives. Every request is
8
+ campaigns and rewards, and the Basic-auth `lead_becomes_referral` webhook our backend receives.
9
+ The legacy **v1** promoters, signup-tracking and campaigns-report routes run over the same store
10
+ (best guesses from the vendor docs, not oracle-verified). Every request is
9
11
  recorded in the journal, so a checkout suite can assert what the post-checkout job sent instead
10
12
  of producing a failed job.
11
13
 
@@ -82,13 +84,45 @@ await server.close()
82
84
  | `PUT /v2/company/commissions/{id}` | `{internal_note?, external_note?}` (the vendor's `CommissionUpdate`) → the updated commission, reflected in `GET /v2/company/commissions`; other fields are not writable. An unknown id is 404 `{message: "Commission not found", code: "not_found"}`. |
83
85
  | `GET /v2/company/referrals` | A bare array of stored referrals and live promoter/campaign metadata. `q` searches email/uid; `ids[]`, `filters[type]=lead\|customer`, `filters[state]`, `filters[promoter_id]`, and inclusive `filters[created_at\|customer_since][from\|to]` filter before paging. `page` starts at 1; `per_page` defaults to 20 (max 100). Exhausted pages and unmatched searches return `[]`. |
84
86
  | `GET /v2/company/referrals/{id}` | The same referral projection as listing. A numeric ID or `find_by=email\|uid` selects it; missing records are 404. |
87
+ | `GET /v2/company/campaigns` | **Best guess, not oracle-verified.** A bare array of the configured campaigns (`PUT /__admin/settings` `campaigns`): `{id, name, color, stats: {referrals_count, sales_count, customers_count, promoters_count, revenue_amount}, landing_url, visitor_cookie_life, ref_token_param, auto_approve_leads, track_ad_traffic, auto_approve_promoters, is_private, is_default, upgrade_details, signup_url, links, rewards_for_promoters, rewards_for_referrals}`. `stats` come from stored live promoters, referrals and sales; `is_default` marks `defaultCampaignId`; `landing_url` is the settings `website`. Account settings the emulator has no state for are fixed: `visitor_cookie_life: 60`, `ref_token_param: "fpr"`, auto-approval on, `track_ad_traffic: "allow"`, not private, `upgrade_details` / `signup_url` null, no `links`. `page` / `per_page` (default 10, max 50) follow the v2 API introduction. |
88
+ | `GET /v2/company/rewards?scope=promoters\|referrals` | **Best guess, not oracle-verified.** A bare array of every campaign reward, by `id`: `{id, name, is_promoter_reward, promoter_reward_type, coupon, editable, hide_reward, provider_coupon_id, duration, duration_in_months, max_redemptions, new_customers_only, created_at, updated_at, provider, tier_reward_mode, tier_level, schemas: [{unit, amount, flat_amount, rules: []}]}`. `scope=promoters` keeps promoter rewards (`promoterRewards`), `scope=referrals` referred-customer rewards (`referralRewards`); any other scope is 422 `{message, code: "invalid_params"}`. A percent reward is `{unit: "cash", amount: <percent>, flat_amount: false}`. Seeded rewards carry a fixed `created_at` of `2024-01-01T00:00:00.000Z`. |
85
89
 
86
90
  The [official referral reference](https://docs.firstpromoter.com/api-reference-v2/api-admin/referrals/get-referrals)
87
91
  specifies the bare array. Paging parameters and limits follow the official
88
92
  [n8n client](https://github.com/firstpromoter/n8n-app/blob/0842d761a3c024e7cf3030e5a108447375f03807/nodes/FirstPromoter/GeneralFunctions.ts)
89
93
  and [input definitions](https://github.com/firstpromoter/n8n-app/blob/0842d761a3c024e7cf3030e5a108447375f03807/nodes/FirstPromoter/FirstPromoter.node.ts).
90
94
 
91
- Every call needs `Authorization: Bearer <key>` and an `Account-ID` header (else 401).
95
+ Every v2 call needs `Authorization: Bearer <key>` and an `Account-ID` header (else 401).
96
+
97
+ The campaign and reward shapes come from the vendor's published v2 OpenAPI
98
+ ([campaigns](https://docs.firstpromoter.com/api-reference-v2/api-advanced/campaigns-api/openapi-v2-campaigns.json),
99
+ [rewards](https://docs.firstpromoter.com/api-reference-v2/api-advanced/rewards/openapi-v2-rewards.json));
100
+ no FirstPromoter credentials were available to compare them with a live account.
101
+
102
+ ### Legacy v1 API (best guess, not oracle-verified)
103
+
104
+ The v1 routes share the v2 store (a promoter created through one API is visible through the
105
+ other). They authenticate with `X-API-KEY` only (no `Account-ID`); a missing key is 401 with the
106
+ documented JSON string body `"Bad credentials"`, and a v2 Bearer token alone is not accepted. As
107
+ documented, parameters travel in the query string; a form or JSON body is read too. Shapes and
108
+ messages follow the vendor's v1 reference
109
+ ([promoters](https://docs.firstpromoter.com/api-reference-v1/promoters-api/openapi-promoters.json),
110
+ [signup tracking](https://docs.firstpromoter.com/api-reference-v1/tracking-api/leads-and-signups.md),
111
+ [campaigns report](https://docs.firstpromoter.com/api-reference-v1/reports-api/campaign.md));
112
+ none of it is oracle-verified. Documented options the emulator has no state for are an
113
+ **emulator limitation**: 501 `{message: "Mockingbird: …", code: "mockingbird_unsupported"}`,
114
+ checked before anything is stored.
115
+
116
+ | Route | Behaviour |
117
+ | --- | --- |
118
+ | `GET /v1/promoters/list?campaign_id=` | Live (unarchived) promoters as v1 promoter objects `{id, status, cust_id, email, created_at, default_promotion_id, default_ref_id, note, auth_token, earnings_balance: {cash}, current_balance, paid_balance, profile, promotions: [{id, campaign_id, ref_id, referral_link, campaign_name, current_offer, current_referral_reward, visitors_count, leads_count, customers_count, sales_count, sales_total, …}], promo_codes: []}`; `status` is `approved` (or `pending`). An unknown `campaign_id` is 422 `{"error": "Campaign not found."}`. No pagination headers. |
119
+ | `GET /v1/promoters/show` | By `id`, `promoter_email`, `cust_id`, `ref_id` or `auth_token` (first given wins). 404 `{"error": "Promoter not found."}`; none given is 422. |
120
+ | `POST /v1/promoters/create` | `email` (422 `{"error": "Email can't be blank"}` / `"Email is invalid"`), `first_name`, `last_name`, `cust_id`, `ref_id` (the ref token), `campaign_id` (default campaign otherwise; unknown is 422 `Campaign not found.`), `note`, `skip_email_notification` (the emulator sends no email anyway). A live duplicate email / `cust_id` / `ref_id` is 422 `{"error": "Email\|Cust\|Ref has already been taken"}`. `promo_code`, `customer_promo_code`, `temp_password`, `parent_promoter_*`, `created_at`, `always_approve` and the profile/social fields are 501. |
121
+ | `PUT /v1/promoters/update` | Finds the promoter by `id`, `cust_id`, `auth_token` or `ref_id`, else by `email`; writes `email` (when it is not the identifier), `first_name`, `last_name`, `new_cust_id`, `new_ref_id` (the default promotion's ref token) and `note`, with the same 422 duplicate checks. `campaign_id`, `promo_code` and the profile/social fields are 501. |
122
+ | `DELETE /v1/promoters/delete` | By `id`, `cust_id`, `ref_id` or `auth_token` → `{"message": "Promoter removed."}`. The emulator archives the promoter (v2 lookups still return it with `archived_at`; v1 show/list no longer do); unknown is 404, none given is 422. |
123
+ | `POST /v1/track/signup` | `email` (+ optional `uid`) attributed by a click `tid` or a `ref_id` → `{id, type: "signup", amount_cents: null, reward: null, lead: {id, state, email, uid, customer_since, …}, promoter: {…}}`. The lead is the same referral v2 lists, and `autoConvert` applies as for v2. Neither `email` nor `uid`: 400 `{"message": "Error. Email and uid can not be blank!"}`; unknown `tid` / `ref_id`: the documented 404 visitor message; an archived promoter: 404 `This promotion is blocked…`; an email or `uid` already tracked: 422 `{"transaction_id": "there is already a signup event registered on this lead"}`. `uid` without `email`, and `promoter_email`, `created_at`, `username`, `first_name`, `last_name`, `website`, `custom_fields`, `sub_id`, `override_existing_lead`, are 501. `ip` is accepted (the emulator has no fraud analysis). |
124
+ | `GET /v1/reports/campaigns` | One row per configured campaign: `{campaign_name, campaign_id, <fields>, items: [{period, <fields>}]}` between `start_date` and `end_date` (`YYYY-MM-DD`, default 12 months ago to today), `group_by=day\|month\|year` (default month; labels `2026-10-04`, `Oct 2026`, `2026`; every period in range is listed). Fields: `selected_fields[]` of `revenue_amount` (sales, as `"$613.17"`), `clicks_count`, `referrals_count` (leads by creation), `customers_count` (by `customer_since`), `sales_count`; default the first four. An unknown field, grouping or date, or day grouping over 90 days, is 422 `{message, code: "invalid_params"}`; the other documented fields (`monthly_churn`, `net_revenue_amount`, the EPC / conversion-rate fields, …) are 501. |
125
+ | `POST /v1/promoters/iframe_login` | **Unsupported (emulator limitation).** The vendor's v1 reference documents no `iframe_login` (only v2 does), so there is no defensible behavior to model: 501 `{message: "Mockingbird: V1IframeLogin is not implemented", code: "mockingbird_unsupported"}`, with no token and no state change. Use `POST /v2/promoters/iframe_login`. |
92
126
 
93
127
  ### Webhooks
94
128
 
@@ -168,15 +202,18 @@ emulator assumption until a live response is captured.
168
202
  ### Namespaces
169
203
 
170
204
  `x-mockingbird-namespace`, a `/__admin/ns/<name>` prefix in `FIRST_PROMOTER_API_URL`, or by API key:
171
- `PUT /__admin/credentials {"credentials": {"<FIRST_PROMOTER_API_KEY>": "<namespace>"}}`.
205
+ `PUT /__admin/credentials {"credentials": {"<FIRST_PROMOTER_API_KEY>": "<namespace>"}}`. The key is the v2
206
+ Bearer token or, for v1 calls, the `X-API-KEY` value.
172
207
 
173
208
  ### Deliberately not modelled
174
209
 
175
210
  - Error bodies and exact status codes of the real API are unverified (no sandbox credentials):
176
211
  errors use `{message}` and validation `{message, errors: {field: [...]}}`. Our consumer only
177
212
  branches on `ok` / 404, which the emulator gets right.
178
- - The v1 API our EMR backend still calls (`/v1/promoters/*`, `/v1/track/signup`,
179
- `/v1/reports/campaigns`); the catalog scopes this emulator to v2.
213
+ - v1 `iframe_login` (undocumented by the vendor, answered 501), the other v1 APIs (leads,
214
+ sales/refund/cancellation tracking, rewards, payouts, promoter and overview reports,
215
+ `move_to_campaign`, `add_to_campaign`, `refresh_token`), v1 pagination headers, and the v1
216
+ options listed above as 501.
180
217
  - Refunds, standalone commission creation/editing/approval, cash payouts and commission filters
181
218
  beyond those listed above; fraud checks;
182
219
  public promo-code provisioning; the hosted affiliate portal behind the iframe token.
package/SUPPORT.md CHANGED
@@ -2,9 +2,9 @@
2
2
 
3
3
  Generated from `openapi.yaml`; do not edit by hand.
4
4
 
5
- - operations in spec: **17**
6
- - supported by the emulator: **17**
7
- - parity enabled: **17**
5
+ - operations in spec: **27**
6
+ - supported by the emulator: **26**
7
+ - parity enabled: **19**
8
8
 
9
9
  | operationId | route | emulator | parity | notes |
10
10
  | --- | --- | --- | --- | --- |
@@ -25,3 +25,13 @@ Generated from `openapi.yaml`; do not edit by hand.
25
25
  | `BatchProcessProgress` | `GET /v2/company/batch_processes/progress` | ✅ supported | ✅ | |
26
26
  | `GetBatchProcess` | `GET /v2/company/batch_processes/{id}` | ✅ supported | ✅ | |
27
27
  | `TrackSale` | `POST /v2/track/sale` | ✅ supported | ⚠️ unsafe (opt-in) | |
28
+ | `ListCampaigns` | `GET /v2/company/campaigns` | ✅ supported | ✅ | Best guess from the vendor docs, not oracle-verified (see README). |
29
+ | `ListRewards` | `GET /v2/company/rewards` | ✅ supported | ✅ | Best guess from the vendor docs, not oracle-verified (see README). |
30
+ | `V1ListPromoters` | `GET /v1/promoters/list` | ✅ supported | ❌ disabled | Best guess from the vendor docs, not oracle-verified (see README). |
31
+ | `V1ShowPromoter` | `GET /v1/promoters/show` | ✅ supported | ❌ disabled | Best guess from the vendor docs, not oracle-verified (see README). |
32
+ | `V1CreatePromoter` | `POST /v1/promoters/create` | ✅ supported | ❌ disabled | Best guess from the vendor docs, not oracle-verified (see README). |
33
+ | `V1UpdatePromoter` | `PUT /v1/promoters/update` | ✅ supported | ❌ disabled | Best guess from the vendor docs, not oracle-verified (see README). |
34
+ | `V1DeletePromoter` | `DELETE /v1/promoters/delete` | ✅ supported | ❌ disabled | Best guess from the vendor docs, not oracle-verified (see README). |
35
+ | `V1IframeLogin` | `POST /v1/promoters/iframe_login` | ❌ unsupported | — | Undocumented in the vendor's v1 API reference; use the v2 iframe_login. |
36
+ | `V1TrackSignup` | `POST /v1/track/signup` | ✅ supported | ❌ disabled | Best guess from the vendor docs, not oracle-verified (see README). |
37
+ | `V1CampaignsReport` | `GET /v1/reports/campaigns` | ✅ supported | ❌ disabled | Best guess from the vendor docs, not oracle-verified (see README). |