@sponson/adapters 0.4.0 → 0.6.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.
Files changed (129) hide show
  1. package/README.md +1 -1
  2. package/dist/algolia.d.ts +6 -0
  3. package/dist/algolia.js +151 -0
  4. package/dist/algolia.js.map +1 -0
  5. package/dist/clerk.js +1 -1
  6. package/dist/clerk.js.map +1 -1
  7. package/dist/cloud_run.d.ts +12 -0
  8. package/dist/cloud_run.js +469 -0
  9. package/dist/cloud_run.js.map +1 -0
  10. package/dist/cloudflare.d.ts +25 -0
  11. package/dist/cloudflare.js +310 -0
  12. package/dist/cloudflare.js.map +1 -0
  13. package/dist/common.d.ts +2 -6
  14. package/dist/common.js.map +1 -1
  15. package/dist/digest.d.ts +46 -0
  16. package/dist/digest.js +129 -0
  17. package/dist/digest.js.map +1 -0
  18. package/dist/digitalocean.d.ts +5 -0
  19. package/dist/digitalocean.js +243 -0
  20. package/dist/digitalocean.js.map +1 -0
  21. package/dist/http-adapter-action.d.ts +63 -0
  22. package/dist/http-adapter-action.js +369 -0
  23. package/dist/http-adapter-action.js.map +1 -0
  24. package/dist/http-adapter-auth.d.ts +94 -0
  25. package/dist/http-adapter-auth.js +440 -0
  26. package/dist/http-adapter-auth.js.map +1 -0
  27. package/dist/http-adapter-client.d.ts +57 -0
  28. package/dist/http-adapter-client.js +163 -0
  29. package/dist/http-adapter-client.js.map +1 -0
  30. package/dist/http-adapter-graphql.d.ts +39 -0
  31. package/dist/http-adapter-graphql.js +157 -0
  32. package/dist/http-adapter-graphql.js.map +1 -0
  33. package/dist/http-adapter-list.d.ts +33 -0
  34. package/dist/http-adapter-list.js +421 -0
  35. package/dist/http-adapter-list.js.map +1 -0
  36. package/dist/http-adapter-resource.d.ts +29 -0
  37. package/dist/http-adapter-resource.js +462 -0
  38. package/dist/http-adapter-resource.js.map +1 -0
  39. package/dist/http-adapter-spec.d.ts +216 -0
  40. package/dist/http-adapter-spec.js +660 -0
  41. package/dist/http-adapter-spec.js.map +1 -0
  42. package/dist/http-adapter.d.ts +16 -0
  43. package/dist/http-adapter.js +28 -0
  44. package/dist/http-adapter.js.map +1 -0
  45. package/dist/http.d.ts +61 -2
  46. package/dist/http.js +79 -18
  47. package/dist/http.js.map +1 -1
  48. package/dist/index.d.ts +13 -0
  49. package/dist/index.js +26 -2
  50. package/dist/index.js.map +1 -1
  51. package/dist/launchdarkly.d.ts +15 -0
  52. package/dist/launchdarkly.js +337 -0
  53. package/dist/launchdarkly.js.map +1 -0
  54. package/dist/manual.d.ts +13 -0
  55. package/dist/manual.js +201 -0
  56. package/dist/manual.js.map +1 -0
  57. package/dist/neon.js +1 -1
  58. package/dist/neon.js.map +1 -1
  59. package/dist/netlify.d.ts +18 -0
  60. package/dist/netlify.js +436 -0
  61. package/dist/netlify.js.map +1 -0
  62. package/dist/planetscale.d.ts +10 -0
  63. package/dist/planetscale.js +311 -0
  64. package/dist/planetscale.js.map +1 -0
  65. package/dist/providers.d.ts +28 -0
  66. package/dist/providers.js +41 -0
  67. package/dist/providers.js.map +1 -0
  68. package/dist/recipes.d.ts +167 -0
  69. package/dist/recipes.js +246 -0
  70. package/dist/recipes.js.map +1 -0
  71. package/dist/secrets.d.ts +44 -0
  72. package/dist/secrets.js +279 -0
  73. package/dist/secrets.js.map +1 -1
  74. package/dist/sigv4.d.ts +56 -0
  75. package/dist/sigv4.js +118 -0
  76. package/dist/sigv4.js.map +1 -0
  77. package/dist/supabase.d.ts +9 -0
  78. package/dist/supabase.js +385 -0
  79. package/dist/supabase.js.map +1 -0
  80. package/dist/vercel.js +1 -1
  81. package/dist/vercel.js.map +1 -1
  82. package/dist/xata.d.ts +8 -0
  83. package/dist/xata.js +142 -0
  84. package/dist/xata.js.map +1 -0
  85. package/dist/xml.d.ts +25 -0
  86. package/dist/xml.js +211 -0
  87. package/dist/xml.js.map +1 -0
  88. package/package.json +11 -4
  89. package/recipes/amplify.yaml +147 -0
  90. package/recipes/auth0.yaml +178 -0
  91. package/recipes/azure_swa.yaml +116 -0
  92. package/recipes/cloudflare.yaml +108 -0
  93. package/recipes/cockroachdb.yaml +132 -0
  94. package/recipes/configcat.yaml +123 -0
  95. package/recipes/datadog.yaml +136 -0
  96. package/recipes/firebase.yaml +84 -0
  97. package/recipes/flagsmith.yaml +98 -0
  98. package/recipes/fly.yaml +97 -0
  99. package/recipes/fly_machines.yaml +67 -0
  100. package/recipes/gcs.yaml +102 -0
  101. package/recipes/github.yaml +124 -0
  102. package/recipes/growthbook.yaml +124 -0
  103. package/recipes/heroku.yaml +98 -0
  104. package/recipes/honeycomb.yaml +89 -0
  105. package/recipes/inngest.yaml +97 -0
  106. package/recipes/kinde.yaml +120 -0
  107. package/recipes/mongodb_atlas.yaml +119 -0
  108. package/recipes/okta.yaml +177 -0
  109. package/recipes/posthog.yaml +127 -0
  110. package/recipes/postmark.yaml +105 -0
  111. package/recipes/postmark_server.yaml +93 -0
  112. package/recipes/prisma.yaml +150 -0
  113. package/recipes/qstash.yaml +134 -0
  114. package/recipes/railway.yaml +130 -0
  115. package/recipes/render.yaml +103 -0
  116. package/recipes/resend.yaml +130 -0
  117. package/recipes/s3.yaml +108 -0
  118. package/recipes/sendgrid.yaml +165 -0
  119. package/recipes/sentry.yaml +158 -0
  120. package/recipes/split.yaml +121 -0
  121. package/recipes/statsig.yaml +124 -0
  122. package/recipes/stripe.yaml +310 -0
  123. package/recipes/stytch.yaml +113 -0
  124. package/recipes/svix.yaml +109 -0
  125. package/recipes/turso.yaml +98 -0
  126. package/recipes/unleash.yaml +149 -0
  127. package/recipes/upstash.yaml +130 -0
  128. package/recipes/vercel.yaml +116 -0
  129. package/recipes/workos.yaml +70 -0
@@ -0,0 +1,310 @@
1
+ # yaml-language-server: $schema=../../../schema/recipe.schema.json
2
+ # Stripe API v1 recipes: a test-mode webhook endpoint per preview, and the catalog objects (products, prices,
3
+ # coupons) a preview's code expects. Format and checklist: docs/adr/0020-recipes.md, CONTRIBUTING.md "Adding a recipe".
4
+ provider: stripe
5
+ title: Stripe
6
+ category: webhooks
7
+ verified_on: "2026-10-11"
8
+ docs:
9
+ - title: Authentication
10
+ url: https://docs.stripe.com/api/authentication
11
+ - title: API keys
12
+ url: https://docs.stripe.com/keys
13
+ - title: Idempotent requests
14
+ url: https://docs.stripe.com/api/idempotent_requests
15
+ - title: Create a webhook endpoint
16
+ url: https://docs.stripe.com/api/webhook_endpoints/create
17
+ - title: List all webhook endpoints
18
+ url: https://docs.stripe.com/api/webhook_endpoints/list
19
+ - title: Update a webhook endpoint
20
+ url: https://docs.stripe.com/api/webhook_endpoints/update
21
+ - title: Delete a webhook endpoint
22
+ url: https://docs.stripe.com/api/webhook_endpoints/delete
23
+ - title: Webhooks in the Dashboard (endpoint limit)
24
+ url: https://docs.stripe.com/development/dashboard/webhooks
25
+ - title: Create a product
26
+ url: https://docs.stripe.com/api/products/create
27
+ - title: Retrieve a product
28
+ url: https://docs.stripe.com/api/products/retrieve
29
+ - title: Delete a product
30
+ url: https://docs.stripe.com/api/products/delete
31
+ - title: Create a price
32
+ url: https://docs.stripe.com/api/prices/create
33
+ - title: List all prices
34
+ url: https://docs.stripe.com/api/prices/list
35
+ - title: Update a price
36
+ url: https://docs.stripe.com/api/prices/update
37
+ - title: Create a coupon
38
+ url: https://docs.stripe.com/api/coupons/create
39
+ api:
40
+ base_url: https://api.stripe.com/v1
41
+ base_url_env: STRIPE_API_URL
42
+ auth: { bearer_env: STRIPE_SECRET_KEY }
43
+ encoding: form
44
+ # A preview must never create live-mode objects (ST2): live keys are refused unless this variable says otherwise.
45
+ refuse_credential: { prefixes: [sk_live_, rk_live_], unless_env: SPONSON_STRIPE_ALLOW_LIVE }
46
+ detect:
47
+ deps: [stripe, "@stripe/"]
48
+ env: ["STRIPE_*"]
49
+ starter:
50
+ - op: webhook_endpoint
51
+ preview_url: url
52
+ values: { name: "preview-${ctx.scope}" }
53
+ assumptions:
54
+ - id: ST1
55
+ verified: true
56
+ text: "The API takes a secret or restricted key as `Authorization: Bearer` (besides Basic), and request bodies form-encoded with bracketed nesting (`metadata[k]=v`, `enabled_events[0]=x`)."
57
+ source: https://docs.stripe.com/api/authentication
58
+ - id: ST2
59
+ verified: true
60
+ text: "Test-mode keys start with `sk_test_` / `rk_test_`, live-mode keys with `sk_live_` / `rk_live_`; objects of one mode are invisible to the other. The recipe refuses the live prefixes (`refuse_credential`) unless `SPONSON_STRIPE_ALLOW_LIVE=1`."
61
+ source: https://docs.stripe.com/keys
62
+ - id: ST3
63
+ verified: false
64
+ text: "Organization keys (`sk_org_…`) and platform-managed keys do not carry the mode in their prefix, so they are not refused: use a test-mode secret or restricted key for previews."
65
+ - id: ST4
66
+ verified: true
67
+ text: "Every v1 `POST` accepts an `Idempotency-Key` (up to 255 characters, kept at least 24 hours); a retry with the same key returns the saved answer, and different parameters under one key are an error."
68
+ source: https://docs.stripe.com/api/idempotent_requests
69
+ - id: ST5
70
+ verified: true
71
+ text: "`POST /v1/webhook_endpoints` takes `url`, `enabled_events` (`['*']` for all), `description`, `metadata` and `api_version`, and answers the endpoint with its signing `secret` populated."
72
+ source: https://docs.stripe.com/api/webhook_endpoints/create
73
+ - id: ST6
74
+ verified: true
75
+ text: "`GET /v1/webhook_endpoints` (`limit` 1–100) answers `{ data: [...], has_more }` whose endpoints carry `url`, `enabled_events`, `description` and `metadata` but no `secret`: the secret exists only in the create's answer, hence the `once` output."
76
+ source: https://docs.stripe.com/api/webhook_endpoints/list
77
+ - id: ST7
78
+ verified: true
79
+ text: "An account has at most 16 webhook endpoints, so one list page of 100 holds them all, and at most 16 previews (minus the account's own endpoints) can have one at a time."
80
+ source: https://docs.stripe.com/development/dashboard/webhooks
81
+ - id: ST8
82
+ verified: true
83
+ text: "`POST /v1/webhook_endpoints/{id}` updates `url`, `enabled_events`, `description`, `metadata` and `disabled` in place (the secret is not rotated); `DELETE` on the same path removes the endpoint."
84
+ source: https://docs.stripe.com/api/webhook_endpoints/update
85
+ - id: ST9
86
+ verified: false
87
+ text: "The API returns `url`, `enabled_events` and `description` as they were written, `enabled_events` in the order sent, so an unchanged endpoint hashes the same on every plan."
88
+ - id: ST10
89
+ verified: true
90
+ text: "`POST /v1/products` takes a client-chosen `id` (unique among the account's products), `name`, `description` and `active`; `GET /v1/products/{id}` retrieves it; `POST /v1/products/{id}` updates it."
91
+ source: https://docs.stripe.com/api/products/create
92
+ - id: ST11
93
+ verified: true
94
+ text: "A product can be deleted only while it has no prices, and prices are never deleted, so the recipe archives a product (`active: false`) instead of deleting it."
95
+ source: https://docs.stripe.com/api/products/delete
96
+ - id: ST12
97
+ verified: false
98
+ text: "An archived product is still returned by `GET /v1/products/{id}`, and its id stays taken: re-applying a destroyed scope finds it and reactivates it as an adopted object, which a later destroy leaves active."
99
+ - id: ST13
100
+ verified: true
101
+ text: "`POST /v1/prices` takes `product`, `currency`, `unit_amount`, `recurring[interval]`, `nickname`, `lookup_key` and `transfer_lookup_key` (which moves the lookup key from the price holding it to the new one)."
102
+ source: https://docs.stripe.com/api/prices/create
103
+ - id: ST14
104
+ verified: true
105
+ text: "`GET /v1/prices` lists active prices by default, filters by up to 10 `lookup_keys` and by `active`, and answers `{ data: [...] }`."
106
+ source: https://docs.stripe.com/api/prices/list
107
+ - id: ST15
108
+ verified: true
109
+ text: "A price's amount, currency and interval cannot change after creation; `POST /v1/prices/{id}` updates `active`, `nickname`, `lookup_key` and `metadata`. There is no delete: the recipe archives (`active: false`)."
110
+ source: https://docs.stripe.com/api/prices/update
111
+ - id: ST16
112
+ verified: true
113
+ text: "`POST /v1/coupons` takes a client-chosen `id`, `name`, `percent_off` or `amount_off` with `currency`, `duration` (`once`, `repeating`, `forever`) and `duration_in_months`."
114
+ source: https://docs.stripe.com/api/coupons/create
115
+ - id: ST17
116
+ verified: false
117
+ text: "`GET /v1/coupons/{id}` retrieves a coupon, `POST /v1/coupons/{id}` updates its `name` and `metadata` only (the discount is fixed), and `DELETE /v1/coupons/{id}` deletes it, after which its id may be used again."
118
+ source: https://docs.stripe.com/api/coupons/create
119
+ - id: ST18
120
+ verified: false
121
+ text: "Stripe must reach the endpoint's URL over public HTTPS: a preview behind deployment protection (Vercel Authentication) receives no events unless it lets Stripe's requests through."
122
+ ops:
123
+ webhook_endpoint:
124
+ kind: resource
125
+ title: Webhook endpoint
126
+ summary: "A test-mode webhook endpoint that sends the preview's events to its own URL, `url` + `path` (usually `{ from: web.preview_url }`). Identified by `name` in its metadata, so a new preview URL updates the endpoint in place. Its signing `secret` exists only in the answer that creates the endpoint: it is a once-only output, passed in the same run to the line that writes it (`STRIPE_WEBHOOK_SECRET` in a `vercel.env` or `netlify.env` line); later runs keep what that line holds, and `apply --recreate <line>` rotates it. Writing the secret rebuilds the preview, and a preview URL output follows the newest deploy, so the next run moves the endpoint to the rebuilt deploy's URL (in place: the secret is kept); a URL that does not change per deploy (`https://deploy-preview-${ctx.pr.number}--<site>.netlify.app`, a custom preview domain) avoids that update. A console edit of its URL, events or description is drift."
127
+ covers: [stripe-webhook]
128
+ params:
129
+ name:
130
+ type: string
131
+ required: true
132
+ pattern: "^[A-Za-z0-9_.:/-]{1,200}$"
133
+ description: "The endpoint's identity, kept in its metadata (`sponson_endpoint`): unique per preview, such as `preview-${ctx.scope}`."
134
+ example: "preview-${ctx.scope}"
135
+ url:
136
+ type: string
137
+ required: true
138
+ pattern: "^https://\\S*[^/]$"
139
+ description: "The preview's base URL, without a trailing slash: usually `{ from: <vercel.env or netlify.env line>.preview_url }`."
140
+ example: https://pr-42.preview.example.com
141
+ path:
142
+ type: string
143
+ default: /api/webhooks/stripe
144
+ pattern: "^/\\S*$"
145
+ description: The path of the app's webhook handler, appended to `url`.
146
+ enabled_events:
147
+ type: list
148
+ default: ["*"]
149
+ description: "The event types sent to the endpoint (`['*']`: all that need no explicit selection)."
150
+ example: [checkout.session.completed, customer.subscription.updated]
151
+ description:
152
+ type: string
153
+ description: A note shown in the Dashboard.
154
+ example: Preview webhook managed by Sponson
155
+ api_version:
156
+ type: string
157
+ description: The Stripe API version events are rendered with (the account's default when left out); sent on create only.
158
+ http:
159
+ find:
160
+ path: /webhook_endpoints?limit=100
161
+ list_path: /data
162
+ match: { /metadata/sponson_endpoint: { param: name } }
163
+ create:
164
+ path: /webhook_endpoints
165
+ idempotency_key: true
166
+ body: { api_version: { param: api_version } }
167
+ update: { method: POST, path: "/webhook_endpoints/{id}" }
168
+ delete: { path: "/webhook_endpoints/{id}" }
169
+ fields:
170
+ url: { join: [{ param: url }, { param: path }] }
171
+ enabled_events: { param: enabled_events }
172
+ description: { param: description }
173
+ outputs:
174
+ secret: { path: /secret, sensitive: true, once: true }
175
+ live:
176
+ params: { url: SPONSON_LIVE_STRIPE_WEBHOOK_URL }
177
+ product:
178
+ kind: resource
179
+ title: Product
180
+ summary: "A test-mode product with an id of the plan's choosing, such as `pro-pr-42`, for the prices a preview sells. Destroy archives it (`active: false`): Stripe never deletes a product that has prices. A console edit of its name, description or active flag is drift."
181
+ covers: [stripe-test-objects]
182
+ params:
183
+ product_id:
184
+ type: string
185
+ required: true
186
+ pattern: "^[A-Za-z0-9_-]{1,100}$"
187
+ description: The product's id, unique in the account, such as `pro-${ctx.scope}`.
188
+ example: "pro-${ctx.scope}"
189
+ name:
190
+ type: string
191
+ required: true
192
+ description: The product's name, shown to customers.
193
+ example: Pro (preview)
194
+ description:
195
+ type: string
196
+ description: The product's description, shown to customers.
197
+ http:
198
+ read: { path: "/products/{product_id}" }
199
+ create: { path: /products, idempotency_key: true, body: { id: { param: product_id } } }
200
+ update: { method: POST }
201
+ delete: { method: POST, body: { active: false } }
202
+ fields:
203
+ name: { param: name }
204
+ description: { param: description }
205
+ active: true
206
+ price:
207
+ kind: resource
208
+ title: Price
209
+ summary: "A test-mode price found by its lookup key (`lookup`, such as `pro-monthly-pr-42`, which the preview's code looks the price up by), product, currency, amount and interval. Amount, currency and interval cannot change on a Stripe price, so changing them in the plan makes a new price that takes over the lookup key, and the old one stays until the scope is destroyed. Destroy archives it (`active: false`): Stripe never deletes a price. A console edit of its nickname is drift."
210
+ covers: [stripe-test-objects]
211
+ params:
212
+ lookup:
213
+ type: string
214
+ required: true
215
+ pattern: "^\\S{1,200}$"
216
+ description: "The price's `lookup_key`: what the preview's code looks it up by, unique among active prices. (Named `lookup` because a plan key ending in `_key` must hold a secret reference.)"
217
+ example: "pro-monthly-${ctx.scope}"
218
+ product:
219
+ type: string
220
+ required: true
221
+ description: "The product's id: usually `{ from: <stripe.product line>.id }`."
222
+ example: pro-pr-42
223
+ currency:
224
+ type: string
225
+ required: true
226
+ pattern: "^[a-z]{3}$"
227
+ description: Three-letter ISO currency code, lower-case.
228
+ example: usd
229
+ unit_amount:
230
+ type: integer
231
+ required: true
232
+ description: The amount in the currency's smallest unit (1500 is 15.00 USD).
233
+ example: 1500
234
+ interval:
235
+ type: string
236
+ enum: [day, week, month, year]
237
+ description: The billing interval of a recurring price; left out, the price is one-time.
238
+ example: month
239
+ nickname:
240
+ type: string
241
+ description: A note on the price, hidden from customers.
242
+ example: Pro monthly (preview)
243
+ http:
244
+ find:
245
+ path: "/prices?lookup_keys[]={lookup}&active=true&limit=10"
246
+ list_path: /data
247
+ match:
248
+ lookup_key: { param: lookup }
249
+ active: true
250
+ product: { param: product }
251
+ currency: { param: currency }
252
+ unit_amount: { param: unit_amount }
253
+ /recurring/interval: { param: interval }
254
+ create: { path: /prices, idempotency_key: true, body: { transfer_lookup_key: true } }
255
+ update: { method: POST, path: "/prices/{id}" }
256
+ delete: { method: POST, path: "/prices/{id}", body: { active: false } }
257
+ fields:
258
+ nickname: { param: nickname }
259
+ live:
260
+ params: { product: SPONSON_LIVE_STRIPE_PRODUCT }
261
+ coupon:
262
+ kind: resource
263
+ title: Coupon
264
+ summary: "A test-mode coupon with an id of the plan's choosing, such as `preview-pr-42`, for the discount codes a preview accepts. Its discount (percent or amount, duration) is set on create and fixed after; give the line a new `coupon_id` to change it. Destroy deletes it. A console edit of its name is drift."
265
+ covers: [stripe-test-objects]
266
+ params:
267
+ coupon_id:
268
+ type: string
269
+ required: true
270
+ pattern: "^[A-Za-z0-9_-]{1,100}$"
271
+ description: The coupon's id (the code applied to a customer), unique in the account.
272
+ example: "preview-${ctx.scope}"
273
+ percent_off:
274
+ type: number
275
+ description: The discount in percent (0–100); or set `amount_off` and `currency`.
276
+ example: 25
277
+ amount_off:
278
+ type: integer
279
+ description: The discount in the currency's smallest unit, with `currency`.
280
+ currency:
281
+ type: string
282
+ pattern: "^[a-z]{3}$"
283
+ description: The currency of `amount_off`.
284
+ duration:
285
+ type: string
286
+ enum: [once, repeating, forever]
287
+ default: once
288
+ description: How long the discount applies to a subscription.
289
+ duration_in_months:
290
+ type: integer
291
+ description: "For `duration: repeating`, the number of months."
292
+ name:
293
+ type: string
294
+ description: The name shown to customers (the id when left out).
295
+ example: Preview discount
296
+ http:
297
+ read: { path: "/coupons/{coupon_id}" }
298
+ create:
299
+ path: /coupons
300
+ idempotency_key: true
301
+ body:
302
+ id: { param: coupon_id }
303
+ percent_off: { param: percent_off }
304
+ amount_off: { param: amount_off }
305
+ currency: { param: currency }
306
+ duration: { param: duration }
307
+ duration_in_months: { param: duration_in_months }
308
+ update: { method: POST }
309
+ fields:
310
+ name: { param: name }
@@ -0,0 +1,113 @@
1
+ # yaml-language-server: $schema=../../../schema/recipe.schema.json
2
+ # Stytch Programmatic Workspace Actions (management API v3) recipes. Format and checklist: docs/adr/0020-recipes.md,
3
+ # CONTRIBUTING.md "Adding a recipe".
4
+ provider: stytch
5
+ title: Stytch
6
+ category: auth
7
+ verified_on: "2026-10-11"
8
+ docs:
9
+ - title: Create redirect URL (PWA v3)
10
+ url: https://stytch.com/docs/api-reference/pwa/api/v3/redirect-urls/create-redirect-url
11
+ - title: Get all redirect URLs (PWA v3)
12
+ url: https://stytch.com/docs/api-reference/pwa/api/v3/redirect-urls/get-all-redirect-urls
13
+ - title: Get redirect URL (PWA v3)
14
+ url: https://stytch.com/docs/api-reference/pwa/api/v3/redirect-urls/get-redirect-url
15
+ - title: Update redirect URL (PWA v3)
16
+ url: https://stytch.com/docs/workspace-management/pwa/update-redirect-url
17
+ - title: Delete redirect URL (PWA v3)
18
+ url: https://stytch.com/docs/api-reference/pwa/api/v3/redirect-urls/delete-redirect-url
19
+ - title: Limits
20
+ url: https://stytch.com/docs/workspace-management/pwa/limits
21
+ api:
22
+ base_url: https://management.stytch.com
23
+ base_url_env: STYTCH_MANAGEMENT_API_URL
24
+ auth: { basic: { user_env: STYTCH_WORKSPACE_KEY_ID, password_env: STYTCH_WORKSPACE_KEY_SECRET } }
25
+ detect:
26
+ deps: [stytch, "@stytch/"]
27
+ env: ["STYTCH_*"]
28
+ starter:
29
+ - op: redirect_url
30
+ where:
31
+ project_slug: "Stytch Dashboard → Project settings: the project's slug"
32
+ url: "the exact redirect URL Stytch receives on a preview: its URL plus the path (a `{ from }` reference cannot carry a path)"
33
+ assumptions:
34
+ - id: SY1
35
+ verified: true
36
+ text: "The management API authenticates with HTTP Basic: a workspace management key's id as the user and its secret as the password."
37
+ source: https://stytch.com/docs/api-reference/pwa/api/v3/redirect-urls/create-redirect-url
38
+ - id: SY2
39
+ verified: true
40
+ text: "`POST /pwa/v3/projects/{project_slug}/environments/{environment_slug}/redirect_urls` takes `url`, `valid_types` (`{ type, is_default }`, type one of LOGIN, INVITE, SIGNUP, RESET_PASSWORD, DISCOVERY) and `do_not_promote_defaults`, and answers `{ request_id, redirect_url: { url, valid_types }, status_code }`."
41
+ source: https://stytch.com/docs/api-reference/pwa/api/v3/redirect-urls/create-redirect-url
42
+ - id: SY3
43
+ verified: true
44
+ text: "`GET …/redirect_urls/all` answers `{ redirect_urls: [{ url, valid_types }], … }`: every redirect URL of the environment, unpaged."
45
+ source: https://stytch.com/docs/api-reference/pwa/api/v3/redirect-urls/get-all-redirect-urls
46
+ - id: SY4
47
+ verified: true
48
+ text: "A redirect URL has no id of its own: `GET`, `PUT` (to change `valid_types`) and `DELETE` on `…/redirect_urls?url=<url>` address it by its URL."
49
+ source: https://stytch.com/docs/api-reference/pwa/api/v3/redirect-urls/delete-redirect-url
50
+ - id: SY5
51
+ verified: true
52
+ text: "A project has at most 100 redirect URLs."
53
+ source: https://stytch.com/docs/workspace-management/pwa/limits
54
+ - id: SY6
55
+ verified: false
56
+ text: "`do_not_promote_defaults: true` on create and delete keeps a preview URL from becoming, or a deletion from changing, the environment's default for a type."
57
+ - id: SY7
58
+ verified: false
59
+ text: "A `PUT` body that also carries `url` (the adapter sends the identifying field back on PUT) is accepted, and `valid_types` read back in the order and form they were written, so an unchanged URL hashes the same on every plan."
60
+ - id: SY8
61
+ verified: false
62
+ text: "Each redirect URL is its own object (no shared list to rewrite), so concurrent previews need no lock."
63
+ ops:
64
+ redirect_url:
65
+ kind: resource
66
+ title: Redirect URL
67
+ summary: "One redirect URL of a Stytch project environment, such as the preview's `/authenticate`, with the redirect types it is valid for (by default login and sign-up, never the default). Found by its URL; a console edit of its types is drift and is put back with a `PUT`. Destroy deletes it without promoting another URL to default."
68
+ covers: [stytch-redirect]
69
+ params:
70
+ project_slug:
71
+ type: string
72
+ required: true
73
+ pattern: "^[A-Za-z0-9_-]+$"
74
+ description: The project's slug.
75
+ example: my-app
76
+ environment_slug:
77
+ type: string
78
+ required: true
79
+ pattern: "^[A-Za-z0-9_-]+$"
80
+ description: The environment's slug within the project (its test environment, or a custom one).
81
+ example: test
82
+ url:
83
+ type: string
84
+ required: true
85
+ pattern: "^https?://\\S+$"
86
+ description: "The redirect URL, exactly as Stytch will receive it (`https://pr-42.preview.example.com/authenticate`)."
87
+ example: "https://${ctx.scope}.preview.example.com/authenticate"
88
+ valid_types:
89
+ type: list
90
+ default: [{ type: LOGIN, is_default: false }, { type: SIGNUP, is_default: false }]
91
+ description: "The redirect types the URL is valid for: `{ type, is_default }` with type LOGIN, INVITE, SIGNUP, RESET_PASSWORD or DISCOVERY."
92
+ http:
93
+ find:
94
+ path: "/pwa/v3/projects/{project_slug}/environments/{environment_slug}/redirect_urls/all"
95
+ list_path: /redirect_urls
96
+ match: { url: { param: url } }
97
+ create:
98
+ path: "/pwa/v3/projects/{project_slug}/environments/{environment_slug}/redirect_urls"
99
+ body: { do_not_promote_defaults: true }
100
+ update:
101
+ method: PUT
102
+ path: "/pwa/v3/projects/{project_slug}/environments/{environment_slug}/redirect_urls?url={id}"
103
+ body: { do_not_promote_defaults: true }
104
+ delete:
105
+ path: "/pwa/v3/projects/{project_slug}/environments/{environment_slug}/redirect_urls?url={id}&do_not_promote_defaults=true"
106
+ item_path: /redirect_url
107
+ id_path: /url
108
+ fields:
109
+ valid_types: { param: valid_types }
110
+ sim:
111
+ item_query: url
112
+ live:
113
+ params: { project_slug: SPONSON_LIVE_STYTCH_PROJECT_SLUG, environment_slug: SPONSON_LIVE_STYTCH_ENVIRONMENT_SLUG }
@@ -0,0 +1,109 @@
1
+ # yaml-language-server: $schema=../../../schema/recipe.schema.json
2
+ # Svix API recipes (Svix as the webhook sender). Format and checklist: docs/adr/0020-recipes.md, CONTRIBUTING.md
3
+ # "Adding a recipe".
4
+ provider: svix
5
+ title: Svix
6
+ category: webhooks
7
+ verified_on: "2026-10-11"
8
+ docs:
9
+ - title: Svix API reference (OpenAPI document)
10
+ url: https://api.svix.com/api/v1/openapi.json
11
+ - title: Svix API reference
12
+ url: https://api.svix.com/docs
13
+ - title: Entities overview (ids and uids)
14
+ url: https://docs.svix.com/overview
15
+ api:
16
+ base_url: https://api.us.svix.com
17
+ base_url_env: SVIX_API_URL
18
+ auth: { bearer_env: SVIX_AUTH_TOKEN }
19
+ detect:
20
+ deps: [svix]
21
+ env: ["SVIX_*"]
22
+ starter:
23
+ - op: endpoint
24
+ values: { uid: "sponson-${ctx.env}-${ctx.scope}" }
25
+ where:
26
+ app_id: "Svix dashboard → Applications: the application (customer) the previews receive as"
27
+ url: "the preview's webhook handler: its URL plus the handler's path (a `{ from }` reference cannot carry a path)"
28
+ assumptions:
29
+ - id: SV1
30
+ verified: true
31
+ text: "`POST /api/v1/app/{app_id}/endpoint` creates an endpoint from `url` (required), `uid`, `description`, `eventTypes`, `channels`, `disabled`, `metadata` and an optional `secret`, answering 201 with the endpoint (`id` `ep_…`, `uid`, `url`, …); requests carry `Authorization: Bearer <token>`."
32
+ source: https://api.svix.com/api/v1/openapi.json
33
+ - id: SV2
34
+ verified: true
35
+ text: "`GET`, `PATCH` (partial update) and `DELETE` (204) `/api/v1/app/{app_id}/endpoint/{endpoint_id}` take the endpoint's id or its uid, and `{app_id}` takes the application's id or uid."
36
+ source: https://api.svix.com/api/v1/openapi.json
37
+ - id: SV3
38
+ verified: true
39
+ text: "A uid is 1–256 characters of letters, digits, `-`, `_` and `.`, unique among the application's endpoints, so it names the endpoint for every later request and a create whose answer was lost is found by it."
40
+ source: https://api.svix.com/api/v1/openapi.json
41
+ - id: SV4
42
+ verified: true
43
+ text: "The endpoint's signing secret is generated when `secret` is null (recommended) and is readable at any time with `GET …/endpoint/{endpoint_id}/secret` (`{ key }`): it is not a once-only value, and this recipe does not output it (an endpoint answer does not carry it)."
44
+ source: https://api.svix.com/api/v1/openapi.json
45
+ - id: SV5
46
+ verified: true
47
+ text: "The API is served per region (`api.us.svix.com`, `api.eu.svix.com`, `api.ca.svix.com`, `api.au.svix.com`), and POST requests accept an `idempotency-key` header."
48
+ source: https://api.svix.com/api/v1/openapi.json
49
+ - id: SV6
50
+ verified: false
51
+ text: "A create with a uid that exists already answers 409 (the specification lists 409 for every operation without saying when), after which the endpoint is found by its uid and claimed only if an earlier intent of the same scope named it."
52
+ - id: SV7
53
+ verified: false
54
+ text: "`description` is answered as sent (default empty), `disabled` as a boolean and `eventTypes` as the list sent, so they compare without drift on every plan; accounts outside the US region set `base_url` in the provider block."
55
+ ops:
56
+ endpoint:
57
+ kind: resource
58
+ title: Application endpoint
59
+ summary: An endpoint of a Svix application delivering to an environment's URL, named by a uid such as `preview-pr-42`. Its URL, description, event type filter and disabled flag are compared for drift; its signing secret stays readable in Svix (SV4).
60
+ covers: [svix-endpoint]
61
+ params:
62
+ app_id:
63
+ type: string
64
+ required: true
65
+ description: The application's id (`app_…`) or uid.
66
+ example: acme-customer
67
+ uid:
68
+ type: string
69
+ required: true
70
+ pattern: "^[a-zA-Z0-9\\-_.]{1,256}$"
71
+ description: The endpoint's uid, unique in the application; its identity.
72
+ example: "sponson-${ctx.env}-${ctx.scope}"
73
+ url:
74
+ type: string
75
+ required: true
76
+ pattern: "^https?://\\S+$"
77
+ description: The URL messages are delivered to.
78
+ example: "https://${ctx.scope}.preview.example.com/api/webhooks"
79
+ description:
80
+ type: string
81
+ default: ""
82
+ description: A note shown in the Svix dashboard and the application portal.
83
+ event_types:
84
+ type: list
85
+ description: Event type names to deliver (all when left out); at least one when set.
86
+ disabled:
87
+ type: boolean
88
+ default: false
89
+ description: Whether deliveries are paused.
90
+ secret:
91
+ type: string
92
+ description: "A signing secret to use instead of a generated one (`whsec_…`); sent on create only. Use `{ secret: … }`."
93
+ http:
94
+ read: { path: "/api/v1/app/{app_id}/endpoint/{uid}" }
95
+ create:
96
+ path: "/api/v1/app/{app_id}/endpoint"
97
+ body: { uid: { param: uid }, secret: { param: secret } }
98
+ idempotency_key: true
99
+ update: { method: PATCH, path: "/api/v1/app/{app_id}/endpoint/{id}" }
100
+ delete: { path: "/api/v1/app/{app_id}/endpoint/{id}" }
101
+ fields:
102
+ url: { param: url }
103
+ description: { param: description }
104
+ disabled: { param: disabled }
105
+ eventTypes: { param: event_types }
106
+ sim:
107
+ id_from: uid
108
+ live:
109
+ params: { app_id: SPONSON_LIVE_SVIX_APP_ID }
@@ -0,0 +1,98 @@
1
+ # yaml-language-server: $schema=../../../schema/recipe.schema.json
2
+ # Turso Platform API recipes. Format and checklist: docs/adr/0020-recipes.md, CONTRIBUTING.md "Adding a recipe".
3
+ provider: turso
4
+ title: Turso
5
+ category: databases
6
+ verified_on: "2026-10-11"
7
+ docs:
8
+ - title: Create database
9
+ url: https://docs.turso.tech/api-reference/databases/create
10
+ - title: Retrieve database
11
+ url: https://docs.turso.tech/api-reference/databases/retrieve
12
+ - title: Delete database
13
+ url: https://docs.turso.tech/api-reference/databases/delete
14
+ - title: Branching
15
+ url: https://docs.turso.tech/features/branching
16
+ api:
17
+ base_url: https://api.turso.tech/v1
18
+ base_url_env: TURSO_API_URL
19
+ auth: { bearer_env: TURSO_API_TOKEN }
20
+ detect:
21
+ deps: ["@libsql/client", "@tursodatabase/"]
22
+ env: ["TURSO_*"]
23
+ db_hosts: [.turso.io]
24
+ starter:
25
+ - op: database_branch
26
+ values: { name: "app-${ctx.scope}" }
27
+ where:
28
+ organization: "`turso org list`"
29
+ parent: "the database to branch from: `turso db list`"
30
+ assumptions:
31
+ - id: TU1
32
+ verified: true
33
+ text: "`POST /v1/organizations/{organizationSlug}/databases` with `name`, `group` and `seed: { type: database, name: <parent> }` creates a database copied from the parent, authenticated with a Platform API token as `Authorization: Bearer`."
34
+ source: https://docs.turso.tech/api-reference/databases/create
35
+ - id: TU2
36
+ verified: true
37
+ text: "The create answers `{ database: { DbId, Hostname, Name } }` (capitalised), and 409 when a database with that name exists already."
38
+ source: https://docs.turso.tech/api-reference/databases/create
39
+ - id: TU3
40
+ verified: true
41
+ text: "`GET /v1/organizations/{organizationSlug}/databases/{databaseName}` answers `{ database: { Name, DbId, Hostname, group, … } }`, and 404 when there is none."
42
+ source: https://docs.turso.tech/api-reference/databases/retrieve
43
+ - id: TU4
44
+ verified: true
45
+ text: "`DELETE` on the same path removes the database; 404 when it is gone."
46
+ source: https://docs.turso.tech/api-reference/databases/delete
47
+ - id: TU5
48
+ verified: true
49
+ text: Database names are lower-case letters, numbers and dashes, at most 64 characters, unique in the organization; the group must exist already.
50
+ source: https://docs.turso.tech/api-reference/databases/create
51
+ - id: TU6
52
+ verified: false
53
+ text: The database is usable as soon as the create answers (no readiness to poll for a branch of a database in the same group).
54
+ ops:
55
+ database_branch:
56
+ kind: resource
57
+ title: Database branch
58
+ summary: "A database copied from a parent database (a branch), named for the scope, such as `app-pr-42` from `app`. Its hostname is an output for the app's connection URL. Only its existence is compared for drift: changing `group` or `parent` later does not move or re-copy a database that exists."
59
+ covers: [turso-database]
60
+ params:
61
+ organization:
62
+ type: string
63
+ required: true
64
+ description: The organization (or user account) slug.
65
+ example: acme
66
+ name:
67
+ type: string
68
+ required: true
69
+ pattern: "^[a-z0-9-]{1,64}$"
70
+ description: The new database's name.
71
+ example: "app-${ctx.scope}"
72
+ parent:
73
+ type: string
74
+ required: true
75
+ description: The parent database to copy.
76
+ example: app
77
+ group:
78
+ type: string
79
+ default: default
80
+ description: The group the database is placed in; it must exist.
81
+ timestamp:
82
+ type: string
83
+ description: Copy the parent as it was at this ISO 8601 time (point-in-time recovery window only).
84
+ http:
85
+ read: { path: "/organizations/{organization}/databases/{name}" }
86
+ create:
87
+ path: "/organizations/{organization}/databases"
88
+ body:
89
+ name: { param: name }
90
+ group: { param: group }
91
+ seed: { type: database, name: { param: parent }, timestamp: { param: timestamp } }
92
+ item_path: /database
93
+ id_path: /Name
94
+ outputs: { hostname: /Hostname, db_id: /DbId }
95
+ sim:
96
+ id_from: name
97
+ live:
98
+ params: { organization: SPONSON_LIVE_TURSO_ORGANIZATION, parent: SPONSON_LIVE_TURSO_DATABASE }