@sponson/adapters 0.4.0 → 0.5.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 (127) 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/cloudflare.d.ts +25 -0
  8. package/dist/cloudflare.js +310 -0
  9. package/dist/cloudflare.js.map +1 -0
  10. package/dist/common.d.ts +2 -6
  11. package/dist/common.js.map +1 -1
  12. package/dist/digest.d.ts +46 -0
  13. package/dist/digest.js +129 -0
  14. package/dist/digest.js.map +1 -0
  15. package/dist/digitalocean.d.ts +5 -0
  16. package/dist/digitalocean.js +243 -0
  17. package/dist/digitalocean.js.map +1 -0
  18. package/dist/http-adapter-action.d.ts +63 -0
  19. package/dist/http-adapter-action.js +369 -0
  20. package/dist/http-adapter-action.js.map +1 -0
  21. package/dist/http-adapter-auth.d.ts +88 -0
  22. package/dist/http-adapter-auth.js +437 -0
  23. package/dist/http-adapter-auth.js.map +1 -0
  24. package/dist/http-adapter-client.d.ts +51 -0
  25. package/dist/http-adapter-client.js +156 -0
  26. package/dist/http-adapter-client.js.map +1 -0
  27. package/dist/http-adapter-graphql.d.ts +39 -0
  28. package/dist/http-adapter-graphql.js +157 -0
  29. package/dist/http-adapter-graphql.js.map +1 -0
  30. package/dist/http-adapter-list.d.ts +33 -0
  31. package/dist/http-adapter-list.js +421 -0
  32. package/dist/http-adapter-list.js.map +1 -0
  33. package/dist/http-adapter-resource.d.ts +29 -0
  34. package/dist/http-adapter-resource.js +462 -0
  35. package/dist/http-adapter-resource.js.map +1 -0
  36. package/dist/http-adapter-spec.d.ts +216 -0
  37. package/dist/http-adapter-spec.js +660 -0
  38. package/dist/http-adapter-spec.js.map +1 -0
  39. package/dist/http-adapter.d.ts +16 -0
  40. package/dist/http-adapter.js +28 -0
  41. package/dist/http-adapter.js.map +1 -0
  42. package/dist/http.d.ts +61 -2
  43. package/dist/http.js +79 -18
  44. package/dist/http.js.map +1 -1
  45. package/dist/index.d.ts +12 -0
  46. package/dist/index.js +24 -2
  47. package/dist/index.js.map +1 -1
  48. package/dist/launchdarkly.d.ts +15 -0
  49. package/dist/launchdarkly.js +337 -0
  50. package/dist/launchdarkly.js.map +1 -0
  51. package/dist/manual.d.ts +13 -0
  52. package/dist/manual.js +201 -0
  53. package/dist/manual.js.map +1 -0
  54. package/dist/neon.js +1 -1
  55. package/dist/neon.js.map +1 -1
  56. package/dist/netlify.d.ts +18 -0
  57. package/dist/netlify.js +436 -0
  58. package/dist/netlify.js.map +1 -0
  59. package/dist/planetscale.d.ts +10 -0
  60. package/dist/planetscale.js +311 -0
  61. package/dist/planetscale.js.map +1 -0
  62. package/dist/providers.d.ts +26 -0
  63. package/dist/providers.js +36 -0
  64. package/dist/providers.js.map +1 -0
  65. package/dist/recipes.d.ts +167 -0
  66. package/dist/recipes.js +246 -0
  67. package/dist/recipes.js.map +1 -0
  68. package/dist/secrets.d.ts +44 -0
  69. package/dist/secrets.js +279 -0
  70. package/dist/secrets.js.map +1 -1
  71. package/dist/sigv4.d.ts +56 -0
  72. package/dist/sigv4.js +118 -0
  73. package/dist/sigv4.js.map +1 -0
  74. package/dist/supabase.d.ts +9 -0
  75. package/dist/supabase.js +385 -0
  76. package/dist/supabase.js.map +1 -0
  77. package/dist/vercel.js +1 -1
  78. package/dist/vercel.js.map +1 -1
  79. package/dist/xata.d.ts +8 -0
  80. package/dist/xata.js +142 -0
  81. package/dist/xata.js.map +1 -0
  82. package/dist/xml.d.ts +25 -0
  83. package/dist/xml.js +211 -0
  84. package/dist/xml.js.map +1 -0
  85. package/package.json +11 -4
  86. package/recipes/amplify.yaml +147 -0
  87. package/recipes/auth0.yaml +178 -0
  88. package/recipes/azure_swa.yaml +116 -0
  89. package/recipes/cloud_run.yaml +97 -0
  90. package/recipes/cloudflare.yaml +108 -0
  91. package/recipes/cockroachdb.yaml +132 -0
  92. package/recipes/configcat.yaml +123 -0
  93. package/recipes/datadog.yaml +136 -0
  94. package/recipes/firebase.yaml +84 -0
  95. package/recipes/flagsmith.yaml +98 -0
  96. package/recipes/fly.yaml +97 -0
  97. package/recipes/fly_machines.yaml +67 -0
  98. package/recipes/gcs.yaml +102 -0
  99. package/recipes/github.yaml +123 -0
  100. package/recipes/growthbook.yaml +124 -0
  101. package/recipes/heroku.yaml +98 -0
  102. package/recipes/honeycomb.yaml +89 -0
  103. package/recipes/inngest.yaml +97 -0
  104. package/recipes/kinde.yaml +120 -0
  105. package/recipes/mongodb_atlas.yaml +119 -0
  106. package/recipes/okta.yaml +177 -0
  107. package/recipes/posthog.yaml +127 -0
  108. package/recipes/postmark.yaml +105 -0
  109. package/recipes/postmark_server.yaml +93 -0
  110. package/recipes/prisma.yaml +150 -0
  111. package/recipes/qstash.yaml +134 -0
  112. package/recipes/railway.yaml +130 -0
  113. package/recipes/render.yaml +103 -0
  114. package/recipes/resend.yaml +130 -0
  115. package/recipes/s3.yaml +108 -0
  116. package/recipes/sendgrid.yaml +165 -0
  117. package/recipes/sentry.yaml +158 -0
  118. package/recipes/split.yaml +121 -0
  119. package/recipes/statsig.yaml +124 -0
  120. package/recipes/stripe.yaml +310 -0
  121. package/recipes/stytch.yaml +113 -0
  122. package/recipes/svix.yaml +109 -0
  123. package/recipes/turso.yaml +98 -0
  124. package/recipes/unleash.yaml +149 -0
  125. package/recipes/upstash.yaml +130 -0
  126. package/recipes/vercel.yaml +116 -0
  127. package/recipes/workos.yaml +70 -0
@@ -0,0 +1,177 @@
1
+ # yaml-language-server: $schema=../../../schema/recipe.schema.json
2
+ # Okta Management API recipes. Format and checklist: docs/adr/0020-recipes.md, CONTRIBUTING.md "Adding a recipe".
3
+ provider: okta
4
+ title: Okta
5
+ category: auth
6
+ verified_on: "2026-10-11"
7
+ docs:
8
+ - title: Okta Management API (OpenAPI specification, management-minimal.yaml)
9
+ url: https://raw.githubusercontent.com/okta/okta-management-openapi-spec/master/dist/current/management-minimal.yaml
10
+ - title: Applications API, replace an application
11
+ url: https://developer.okta.com/docs/api/openapi/okta-management/management/tag/Application/#tag/Application/operation/replaceApplication
12
+ - title: Trusted Origins API
13
+ url: https://developer.okta.com/docs/api/openapi/okta-management/management/tag/TrustedOrigin/
14
+ - title: Okta Terraform provider, configuration (conventional variable names)
15
+ url: https://raw.githubusercontent.com/okta/terraform-provider-okta/master/docs/index.md
16
+ api:
17
+ base_url_env: OKTA_API_URL
18
+ auth: { header: Authorization, value_env: OKTA_API_TOKEN, prefix: "SSWS " }
19
+ base_url_example: https://acme.okta.com/api/v1
20
+ detect:
21
+ deps: ["@okta/"]
22
+ env: ["OKTA_*"]
23
+ starter:
24
+ - op: redirect_uri
25
+ - op: trusted_origin
26
+ preview_url: origin
27
+ values: { name: "sponson-${ctx.scope}" }
28
+ where:
29
+ base_url: "`https://<org>.okta.com/api/v1`: your Okta org's domain"
30
+ app_id: "Okta Admin Console → Applications → your application: the `0oa…` id in its URL"
31
+ url: "the exact redirect URI the app sends on a preview: its URL plus the callback path (a `{ from }` reference cannot carry a path)"
32
+ assumptions:
33
+ - id: OK1
34
+ verified: true
35
+ text: "An API token is sent as `Authorization: SSWS <token>` (the `apiToken` security scheme; Okta recommends scoped OAuth tokens, which need `private_key_jwt` and are not supported here). Every path is under `https://<org>.okta.com/api/v1`."
36
+ source: https://raw.githubusercontent.com/okta/okta-management-openapi-spec/master/dist/current/management-minimal.yaml
37
+ - id: OK2
38
+ verified: true
39
+ text: "`PUT /api/v1/apps/{appId}` replaces an application: all required properties must be in the body (no partial update), and system-assigned properties (`id`, `name`, `status`, `created`, `lastUpdated`) in the body are ignored. So the recipe sends the whole application read with `GET /api/v1/apps/{appId}` back, with one URI added or removed (`parent.send: parent`)."
40
+ source: https://raw.githubusercontent.com/okta/okta-management-openapi-spec/master/dist/current/management-minimal.yaml
41
+ - id: OK3
42
+ verified: true
43
+ text: "An OIDC application keeps its redirect URIs in `settings.oauthClient.redirect_uris` and its sign-out redirect URIs in `settings.oauthClient.post_logout_redirect_uris`, both arrays of strings."
44
+ source: https://raw.githubusercontent.com/okta/okta-management-openapi-spec/master/dist/current/management-minimal.yaml
45
+ - id: OK4
46
+ verified: false
47
+ text: "The read-only members of the GET answer that the replace documentation does not list (`_links`, `_embedded`, `credentials.oauthClient.client_id`) are ignored when sent back, as the listed system-assigned ones are."
48
+ - id: OK5
49
+ verified: false
50
+ text: "The application replace has no precondition: the line locks the application (`lockOn`, shared by both URI ops) against other Sponson runs; a console edit made between the read and the write can still be lost (the next apply reports it as drift)."
51
+ - id: OK6
52
+ verified: true
53
+ text: "`POST /api/v1/trustedOrigins` creates a trusted origin from `name`, `origin` and `scopes` (`[{ type: CORS }, { type: REDIRECT }]`, or `IFRAME_EMBED`); `GET /api/v1/trustedOrigins` lists them as a bare array, `q` prefix-matching `name` and `origin`, `limit` up to 200; `PUT /api/v1/trustedOrigins/{id}` replaces one; `DELETE /api/v1/trustedOrigins/{id}` answers 204. `name` and `origin` are each unique."
54
+ source: https://raw.githubusercontent.com/okta/okta-management-openapi-spec/master/dist/current/management-minimal.yaml
55
+ - id: OK7
56
+ verified: false
57
+ text: "A trusted origin is found among the first 200 that `q=<origin>` matches (pagination follows a `Link` header, which the http adapter does not read); an org with more than 200 origins starting with the same text may not find it and would see the create refused as a duplicate."
58
+ - id: OK8
59
+ verified: false
60
+ text: "Scopes are read back as written (`[{ type: CORS }, { type: REDIRECT }]`, no added members), so they can be compared as drift."
61
+ - id: OK9
62
+ verified: true
63
+ text: "`OKTA_API_TOKEN` is the variable the Okta Terraform provider reads for an API token."
64
+ source: https://raw.githubusercontent.com/okta/terraform-provider-okta/master/docs/index.md
65
+ ops:
66
+ redirect_uri:
67
+ kind: list_item
68
+ title: OIDC application sign-in redirect URI
69
+ summary: "One URI in an Okta OIDC application's sign-in redirect URIs (`settings.oauthClient.redirect_uris`), such as the preview's `/login/callback`. The application is read, and written back whole with the URI added (Okta replaces applications only as a whole), under a lock on the application; removed on destroy; every other URI and setting is sent back as read."
70
+ covers: [okta-redirect]
71
+ params:
72
+ app_id:
73
+ type: string
74
+ required: true
75
+ description: The application's id (`0oa…`).
76
+ example: 0oa1gjh63g214q0Hq0g4
77
+ url:
78
+ type: string
79
+ required: true
80
+ pattern: "^\\S+://\\S+$"
81
+ description: "The redirect URI, exactly as the application sends it as `redirect_uri`."
82
+ example: "https://${ctx.scope}.preview.example.app/login/callback"
83
+ http:
84
+ parent: { path: "/apps/{app_id}", method: PUT, send: parent }
85
+ list_path: /settings/oauthClient/redirect_uris
86
+ item: { param: url }
87
+ sim:
88
+ objects:
89
+ "/apps/{app_id}":
90
+ id: 0oa1gjh63g214q0Hq0g4
91
+ name: oidc_client
92
+ label: Web app
93
+ status: ACTIVE
94
+ signOnMode: OPENID_CONNECT
95
+ credentials: { oauthClient: { client_id: 0oa1gjh63g214q0Hq0g4, token_endpoint_auth_method: client_secret_basic } }
96
+ settings:
97
+ oauthClient:
98
+ application_type: web
99
+ grant_types: [authorization_code]
100
+ response_types: [code]
101
+ redirect_uris: [https://app.example.com/login/callback]
102
+ post_logout_redirect_uris: [https://app.example.com]
103
+ live:
104
+ params: { app_id: SPONSON_LIVE_OKTA_APP_ID }
105
+ post_logout_redirect_uri:
106
+ kind: list_item
107
+ title: OIDC application sign-out redirect URI
108
+ summary: "One URI in an Okta OIDC application's sign-out redirect URIs (`settings.oauthClient.post_logout_redirect_uris`), such as the preview's home page. Written back with the whole application, under the application's lock; removed on destroy; every other URI and setting is sent back as read."
109
+ covers: [okta-redirect]
110
+ params:
111
+ app_id:
112
+ type: string
113
+ required: true
114
+ description: The application's id (`0oa…`).
115
+ example: 0oa1gjh63g214q0Hq0g4
116
+ url:
117
+ type: string
118
+ required: true
119
+ pattern: "^\\S+://\\S+$"
120
+ description: "The URI the application passes as `post_logout_redirect_uri`."
121
+ example: "https://${ctx.scope}.preview.example.app"
122
+ http:
123
+ parent: { path: "/apps/{app_id}", method: PUT, send: parent }
124
+ list_path: /settings/oauthClient/post_logout_redirect_uris
125
+ item: { param: url }
126
+ sim:
127
+ objects:
128
+ "/apps/{app_id}":
129
+ id: 0oa1gjh63g214q0Hq0g4
130
+ name: oidc_client
131
+ label: Web app
132
+ status: ACTIVE
133
+ signOnMode: OPENID_CONNECT
134
+ credentials: { oauthClient: { client_id: 0oa1gjh63g214q0Hq0g4, token_endpoint_auth_method: client_secret_basic } }
135
+ settings:
136
+ oauthClient:
137
+ application_type: web
138
+ grant_types: [authorization_code]
139
+ response_types: [code]
140
+ redirect_uris: [https://app.example.com/login/callback]
141
+ post_logout_redirect_uris: [https://app.example.com]
142
+ live:
143
+ params: { app_id: SPONSON_LIVE_OKTA_APP_ID }
144
+ trusted_origin:
145
+ kind: resource
146
+ title: Trusted origin (CORS and redirect)
147
+ summary: "A trusted origin for the preview, so the Okta Sign-In Widget and Auth JS may call Okta from it (CORS) and Okta may redirect to it after sign-in or sign-out (REDIRECT). Found by its origin; its name and scopes are compared as drift and replaced with `PUT`; deleted on destroy."
148
+ covers: [okta-redirect]
149
+ params:
150
+ name:
151
+ type: string
152
+ required: true
153
+ description: "The trusted origin's name, unique in the org."
154
+ example: "sponson-${ctx.scope}"
155
+ origin:
156
+ type: string
157
+ required: true
158
+ pattern: "^\\S+://[^/\\s]+$"
159
+ description: "The origin, scheme and host without a path (`https://pr-42.preview.example.app`); unique in the org."
160
+ example: "https://${ctx.scope}.preview.example.app"
161
+ scopes:
162
+ type: list
163
+ default: [{ type: CORS }, { type: REDIRECT }]
164
+ description: "What the origin is trusted for: `[{ type: CORS }]`, `[{ type: REDIRECT }]`, both (the default), or `IFRAME_EMBED`."
165
+ http:
166
+ find:
167
+ path: "/trustedOrigins?q={origin}&limit=200"
168
+ list_path: ""
169
+ match: { origin: { param: origin } }
170
+ create: { path: /trustedOrigins }
171
+ update: { method: PUT, path: "/trustedOrigins/{id}" }
172
+ delete: { path: "/trustedOrigins/{id}" }
173
+ item_path: ""
174
+ fields:
175
+ name: { param: name }
176
+ origin: { param: origin }
177
+ scopes: { param: scopes }
@@ -0,0 +1,127 @@
1
+ # yaml-language-server: $schema=../../../schema/recipe.schema.json
2
+ # PostHog private API recipes. Format and checklist: docs/adr/0020-recipes.md, CONTRIBUTING.md "Adding a recipe".
3
+ provider: posthog
4
+ title: PostHog
5
+ category: flags
6
+ verified_on: "2026-10-11"
7
+ docs:
8
+ - title: Private API (hosts, personal API keys)
9
+ url: https://posthog.com/docs/api
10
+ - title: Feature flags API
11
+ url: https://posthog.com/docs/api/feature-flags
12
+ - title: OpenAPI schema (FeatureFlag, PersonalAPIKeyAuth)
13
+ url: https://us.posthog.com/api/schema/?format=json
14
+ - title: Feature flag serializer (filters validation and merge)
15
+ url: https://github.com/PostHog/posthog/blob/master/products/feature_flags/backend/api/feature_flag.py
16
+ - title: Feature flag filters schema (condition groups)
17
+ url: https://github.com/PostHog/posthog/blob/master/products/feature_flags/backend/api/filters_schema.py
18
+ api:
19
+ base_url: https://us.posthog.com
20
+ base_url_env: POSTHOG_API_URL
21
+ auth: { bearer_env: POSTHOG_PERSONAL_API_KEY }
22
+ detect:
23
+ deps: [posthog-js, posthog-node, posthog-react-native]
24
+ env: ["POSTHOG_*"]
25
+ starter:
26
+ - op: release_condition
27
+ values: { property: $host }
28
+ where:
29
+ project_id: "PostHog → Settings → Project → Project ID"
30
+ flag_id: "PostHog → Feature flags → your flag: the number in its URL"
31
+ value: "the preview's host name (with `property: $host`)"
32
+ assumptions:
33
+ - id: PH1
34
+ verified: true
35
+ text: "The private API is at `https://us.posthog.com` (US cloud) or `https://eu.posthog.com` (EU cloud; set `base_url`), authenticated with a personal API key as `Authorization: Bearer`, with the `feature_flag:read` and `feature_flag:write` scopes."
36
+ source: https://posthog.com/docs/api
37
+ - id: PH2
38
+ verified: true
39
+ text: "`GET /api/projects/{project_id}/feature_flags/{id}/` (trailing slash) answers the flag bare, with its release conditions in `filters.groups`; `PATCH` on the same path updates it and answers it bare."
40
+ source: https://us.posthog.com/api/schema/?format=json
41
+ - id: PH3
42
+ verified: true
43
+ text: "A condition group is `{ properties: [{ key, type, operator, value }], rollout_percentage, variant }`; `exact` takes a list of values. Groups have no id, so the group is identified by the first value of its first property (the preview host)."
44
+ source: https://github.com/PostHog/posthog/blob/master/products/feature_flags/backend/api/filters_schema.py
45
+ - id: PH4
46
+ verified: true
47
+ text: "A PATCH of `filters` merges top-level keys into the stored filters (`{**stored_filters, **filters}`): sending `{ filters: { groups } }` replaces the groups and keeps `multivariate`, `payloads` and the rest. This is in the serializer's source, not the docs."
48
+ source: https://github.com/PostHog/posthog/blob/master/products/feature_flags/backend/api/feature_flag.py
49
+ - id: PH5
50
+ verified: false
51
+ text: "Self-hosted or older PostHog versions merge `filters` the same way (the merge arrived with 2026's validation work). On a version that replaces `filters` whole, every write would drop the flag's variants and payloads: check before pointing the recipe at one."
52
+ - id: PH6
53
+ verified: false
54
+ text: "A group is read back as written (the serializer drops unknown keys and adds none to the declared ones), so the compared `properties`, `rollout_percentage` and `variant` do not show as changed on every plan."
55
+ - id: PH7
56
+ verified: true
57
+ text: "A flag under an approval policy answers a PATCH with 409 (`approval_required` or `change_request_pending`); the apply then fails with PROVIDER_CONFLICT rather than waiting for the approval."
58
+ source: https://github.com/PostHog/posthog/blob/master/products/approvals/backend/decorators.py
59
+ ops:
60
+ release_condition:
61
+ kind: list_item
62
+ title: Flag release condition
63
+ summary: "A release condition (condition group) on an existing feature flag that matches the preview, such as `$host exact [pr-42.preview.example.com]` at 100%. Groups are OR'd; the flag's other groups, variants and payloads are left as they are. A console edit of its properties, rollout or variant is drift. Destroy removes the group (with the last group gone, the flag matches no one)."
64
+ covers: [posthog-release-condition]
65
+ params:
66
+ project_id:
67
+ type: string
68
+ required: true
69
+ pattern: "^[0-9]+$"
70
+ description: The project's numeric id (Project settings).
71
+ example: "12345"
72
+ flag_id:
73
+ type: string
74
+ required: true
75
+ pattern: "^[0-9]+$"
76
+ description: The flag's numeric id (in the flag's URL), not its key.
77
+ example: "678"
78
+ property:
79
+ type: string
80
+ required: true
81
+ description: The property the condition tests. It must be known when the flag is evaluated (a person property, or one passed with the evaluation).
82
+ example: $host
83
+ property_type:
84
+ type: string
85
+ default: person
86
+ enum: [person, group]
87
+ description: Whether `property` is a person or a group property.
88
+ operator:
89
+ type: string
90
+ default: exact
91
+ enum: [exact, is_not, icontains, regex]
92
+ description: How the property is compared with `value`.
93
+ value:
94
+ type: string
95
+ required: true
96
+ description: The value that matches the preview (such as its host); it identifies the condition on the flag.
97
+ example: "${ctx.scope}.preview.example.com"
98
+ rollout_percentage:
99
+ type: integer
100
+ default: 100
101
+ description: The share of matching users the flag is on for (0–100).
102
+ variant:
103
+ type: string
104
+ description: For a multivariate flag, the variant the condition serves.
105
+ http:
106
+ parent: { path: "/api/projects/{project_id}/feature_flags/{flag_id}/", method: PATCH }
107
+ list_path: /filters/groups
108
+ key_field: /properties/0/value/0
109
+ item:
110
+ properties:
111
+ - key: { param: property }
112
+ type: { param: property_type }
113
+ operator: { param: operator }
114
+ value: [{ param: value }]
115
+ rollout_percentage: { param: rollout_percentage }
116
+ variant: { param: variant }
117
+ sim:
118
+ objects:
119
+ "/api/projects/{project_id}/feature_flags/{flag_id}":
120
+ id: 678
121
+ key: new-checkout
122
+ filters:
123
+ groups: [{ properties: [], rollout_percentage: 0 }]
124
+ multivariate: null
125
+ payloads: {}
126
+ live:
127
+ params: { project_id: SPONSON_LIVE_POSTHOG_PROJECT_ID, flag_id: SPONSON_LIVE_POSTHOG_FLAG_ID }
@@ -0,0 +1,105 @@
1
+ # yaml-language-server: $schema=../../../schema/recipe.schema.json
2
+ # Postmark Account API recipes (account token). The Server API, which takes a server's own token, is
3
+ # postmark_server.yaml. Format and checklist: docs/adr/0020-recipes.md, CONTRIBUTING.md "Adding a recipe".
4
+ provider: postmark
5
+ title: Postmark (Account API)
6
+ category: email
7
+ verified_on: "2026-10-11"
8
+ docs:
9
+ - title: Servers API
10
+ url: https://postmarkapp.com/developer/api/servers-api
11
+ - title: API overview (authentication)
12
+ url: https://postmarkapp.com/developer/api/overview
13
+ api:
14
+ base_url: https://api.postmarkapp.com
15
+ base_url_env: POSTMARK_API_URL
16
+ auth: { header: X-Postmark-Account-Token, value_env: POSTMARK_ACCOUNT_TOKEN }
17
+ detect:
18
+ deps: [postmark]
19
+ env: ["POSTMARK_*"]
20
+ starter:
21
+ - op: server
22
+ values: { name: "sponson-${ctx.env}-${ctx.scope}" }
23
+ assumptions:
24
+ - id: PM1
25
+ verified: true
26
+ text: "The Servers API authenticates with the account token in `X-Postmark-Account-Token` (account owner only) and takes and answers JSON."
27
+ source: https://postmarkapp.com/developer/api/servers-api
28
+ - id: PM2
29
+ verified: true
30
+ text: "`POST /servers` creates a server from `Name` (required), `Color`, `DeliveryType` (`Live` or `Sandbox`, fixed after creation), `InboundHookUrl`, `TrackOpens`, `TrackLinks` and more, answering the server with its integer `ID`, its settings and `ApiTokens` (the server's tokens)."
31
+ source: https://postmarkapp.com/developer/api/servers-api
32
+ - id: PM3
33
+ verified: true
34
+ text: "`GET /servers?count=&offset=&name=` answers `{ TotalCount, Servers: [...] }`; `name` is a substring search (`MyServer` also matches `MyServer Production`), so the recipe matches the exact `Name` among the results."
35
+ source: https://postmarkapp.com/developer/api/servers-api
36
+ - id: PM4
37
+ verified: true
38
+ text: "`PUT /servers/{serverid}` changes the settings sent (everything but `DeliveryType`); `GET /servers/{serverid}` answers the server with its `ApiTokens`, so the server token stays readable."
39
+ source: https://postmarkapp.com/developer/api/servers-api
40
+ - id: PM5
41
+ verified: true
42
+ text: "`DELETE /servers/{serverid}` removes a server, but is not enabled for every account: where Postmark support has not enabled it, destroy fails and the line needs `destroy: keep`."
43
+ source: https://postmarkapp.com/developer/api/servers-api
44
+ - id: PM6
45
+ verified: false
46
+ text: "Server names are unique in the account (a second create with the same name is refused), and an account has fewer than 500 servers, so the first page of `count=500` holds the one `find` looks for."
47
+ - id: PM7
48
+ verified: false
49
+ text: "`Color`, `TrackOpens`, `TrackLinks` and `InboundHookUrl` are answered as sent (`InboundHookUrl` empty when unset), so they compare without drift."
50
+ ops:
51
+ server:
52
+ kind: resource
53
+ title: Server
54
+ summary: "A Postmark server per environment, such as `sponson-preview-pr-42`, with its inbound webhook URL and tracking settings. Its token is a sensitive output for the app's environment (`{ from: <line>.server_token }`). Located by its name; a console edit of its color, tracking or inbound hook URL is drift. Its webhooks: `postmark_server.webhook` with `credential: { from: <this line>.server_token }`."
55
+ covers: [postmark-server-webhook]
56
+ params:
57
+ name:
58
+ type: string
59
+ required: true
60
+ description: The server's name, unique in the account (PM6); its identity.
61
+ example: "sponson-${ctx.env}-${ctx.scope}"
62
+ color:
63
+ type: string
64
+ default: Blue
65
+ enum: [Purple, Blue, Turquoise, Green, Red, Yellow, Grey, Orange]
66
+ description: The server's color in the Postmark dashboard.
67
+ delivery_type:
68
+ type: string
69
+ default: Sandbox
70
+ enum: [Live, Sandbox]
71
+ description: "`Sandbox` accepts mail without delivering it; fixed once the server exists (sent on create only)."
72
+ inbound_hook_url:
73
+ type: string
74
+ default: ""
75
+ description: The URL inbound mail is posted to (empty for none).
76
+ example: "https://${ctx.scope}.preview.example.com/api/postmark/inbound"
77
+ track_opens:
78
+ type: boolean
79
+ default: false
80
+ description: Track opens of outbound mail.
81
+ track_links:
82
+ type: string
83
+ default: None
84
+ enum: [None, HtmlAndText, HtmlOnly, TextOnly]
85
+ description: Track link clicks of outbound mail.
86
+ http:
87
+ find:
88
+ path: "/servers?count=500&offset=0&name={name}"
89
+ list_path: /Servers
90
+ match: { Name: { param: name } }
91
+ create:
92
+ path: /servers
93
+ body: { DeliveryType: { param: delivery_type } }
94
+ update: { method: PUT, path: "/servers/{id}" }
95
+ delete: { path: "/servers/{id}" }
96
+ id_path: /ID
97
+ fields:
98
+ Color: { param: color }
99
+ InboundHookUrl: { param: inbound_hook_url }
100
+ TrackOpens: { param: track_opens }
101
+ TrackLinks: { param: track_links }
102
+ outputs:
103
+ server_token: { path: /ApiTokens/0, sensitive: true }
104
+ sim:
105
+ numeric_ids: true
@@ -0,0 +1,93 @@
1
+ # yaml-language-server: $schema=../../../schema/recipe.schema.json
2
+ # Postmark Server API recipes (one server's token). The Account API is postmark.yaml. Format and checklist:
3
+ # docs/adr/0020-recipes.md, CONTRIBUTING.md "Adding a recipe".
4
+ provider: postmark_server
5
+ title: Postmark (Server API)
6
+ category: email
7
+ verified_on: "2026-10-11"
8
+ docs:
9
+ - title: Webhooks API
10
+ url: https://postmarkapp.com/developer/api/webhooks-api
11
+ - title: API overview (authentication)
12
+ url: https://postmarkapp.com/developer/api/overview
13
+ api:
14
+ base_url: https://api.postmarkapp.com
15
+ base_url_env: POSTMARK_API_URL
16
+ auth: { header: X-Postmark-Server-Token, value_env: POSTMARK_SERVER_TOKEN }
17
+ assumptions:
18
+ - id: PS1
19
+ verified: true
20
+ text: "The Webhooks API authenticates with a server's token in `X-Postmark-Server-Token`, so it manages the webhooks of that one server."
21
+ source: https://postmarkapp.com/developer/api/webhooks-api
22
+ - id: PS2
23
+ verified: true
24
+ text: "`POST /webhooks` creates a webhook from `Url` (required), `MessageStream` (default `outbound`), `HttpAuth`, `HttpHeaders` and `Triggers` (`Open { Enabled, PostFirstOpenOnly }`, `Click`, `Delivery`, `Bounce { Enabled, IncludeContent }`, `SpamComplaint { Enabled, IncludeContent }`, `SubscriptionChange`, each with `Enabled`), answering the webhook with its integer `ID`."
25
+ source: https://postmarkapp.com/developer/api/webhooks-api
26
+ - id: PS3
27
+ verified: true
28
+ text: "`GET /webhooks` (optionally `?MessageStream=`) answers `{ Webhooks: [...] }` with each webhook's `ID`, `Url`, `MessageStream` and `Triggers`."
29
+ source: https://postmarkapp.com/developer/api/webhooks-api
30
+ - id: PS4
31
+ verified: true
32
+ text: "`PUT /webhooks/{Id}` changes `Url`, `HttpAuth`, `HttpHeaders` and `Triggers` (not `MessageStream`), re-verifying the endpoint when the URL or the triggers change; `DELETE /webhooks/{Id}` removes it."
33
+ source: https://postmarkapp.com/developer/api/webhooks-api
34
+ - id: PS5
35
+ verified: true
36
+ text: "Postmark tests the endpoint when a webhook is created; `Verify: false` saves it without the test (a preview that is not deployed yet would fail it)."
37
+ source: https://postmarkapp.com/developer/api/webhooks-api
38
+ - id: PS6
39
+ verified: false
40
+ text: "A URL identifies one webhook of the server (two webhooks with the same URL, on different streams, make `find` refuse with PARAM_INVALID), and every trigger's `Enabled` is answered as a boolean even when it was not sent."
41
+ - id: PS7
42
+ verified: false
43
+ text: "`PostFirstOpenOnly` and `IncludeContent` are left at Postmark's defaults; only which events are delivered is compared."
44
+ - id: PS8
45
+ verified: false
46
+ text: "A server's webhooks belong to it: deleting the server (`DELETE /servers/{id}`, Account API) removes them, so a webhook line authenticated with the server's own token can leave its destroy to the server's line."
47
+ ops:
48
+ webhook:
49
+ kind: resource
50
+ title: Webhook of a server
51
+ summary: "A webhook of one Postmark server delivering to an environment's URL, such as the preview's `/api/postmark` handler, with the events it enables. Located by its URL; a console edit of an event's `Enabled` is drift. The server is the one whose token is in `POSTMARK_SERVER_TOKEN` (a shared staging server), or, for a server the same plan creates per environment (`postmark.server`), the line says `credential: { from: <server line>.server_token }` and `destroy: keep`: deleting the server removes its webhooks (PS8)."
52
+ covers: [postmark-server-webhook]
53
+ params:
54
+ url:
55
+ type: string
56
+ required: true
57
+ pattern: "^https?://\\S+$"
58
+ description: The URL events are posted to; its identity (PS6).
59
+ example: "https://${ctx.scope}.preview.example.com/api/postmark"
60
+ message_stream:
61
+ type: string
62
+ default: outbound
63
+ description: The message stream whose events are delivered (sent on create only).
64
+ verify:
65
+ type: boolean
66
+ default: false
67
+ description: Have Postmark test the URL when the webhook is created (PS5).
68
+ delivery: { type: boolean, default: true, description: Deliver `Delivery` events. }
69
+ bounce: { type: boolean, default: true, description: Deliver `Bounce` events. }
70
+ spam_complaint: { type: boolean, default: true, description: Deliver `SpamComplaint` events. }
71
+ open: { type: boolean, default: false, description: Deliver `Open` events. }
72
+ click: { type: boolean, default: false, description: Deliver `Click` events. }
73
+ subscription_change: { type: boolean, default: false, description: Deliver `SubscriptionChange` events. }
74
+ http:
75
+ find:
76
+ path: /webhooks
77
+ list_path: /Webhooks
78
+ match: { Url: { param: url } }
79
+ create:
80
+ path: /webhooks
81
+ body: { MessageStream: { param: message_stream }, Verify: { param: verify } }
82
+ update: { method: PUT, path: "/webhooks/{id}" }
83
+ delete: { path: "/webhooks/{id}" }
84
+ id_path: /ID
85
+ fields:
86
+ /Triggers/Delivery/Enabled: { param: delivery }
87
+ /Triggers/Bounce/Enabled: { param: bounce }
88
+ /Triggers/SpamComplaint/Enabled: { param: spam_complaint }
89
+ /Triggers/Open/Enabled: { param: open }
90
+ /Triggers/Click/Enabled: { param: click }
91
+ /Triggers/SubscriptionChange/Enabled: { param: subscription_change }
92
+ sim:
93
+ numeric_ids: true
@@ -0,0 +1,150 @@
1
+ # yaml-language-server: $schema=../../../schema/recipe.schema.json
2
+ # Prisma Postgres Management API recipes. Format and checklist: docs/adr/0020-recipes.md, CONTRIBUTING.md "Adding a recipe".
3
+ provider: prisma
4
+ title: Prisma Postgres
5
+ category: databases
6
+ verified_on: "2026-10-11"
7
+ docs:
8
+ - title: Management API OpenAPI document
9
+ url: https://api.prisma.io/v1/doc
10
+ - title: Management API guide (base URL, service tokens)
11
+ url: https://www.prisma.io/docs/guides/management-api-basic
12
+ - title: Connecting to your database (connection strings)
13
+ url: https://www.prisma.io/docs/postgres/database/connecting-to-your-database
14
+ api:
15
+ base_url: https://api.prisma.io/v1
16
+ base_url_env: PRISMA_API_URL
17
+ auth: { bearer_env: PRISMA_SERVICE_TOKEN }
18
+ detect:
19
+ deps: ["@prisma/ppg"]
20
+ db_hosts: [.prisma.io, .prisma-data.net]
21
+ starter:
22
+ - op: database
23
+ values: { name: "app-${ctx.scope}" }
24
+ where:
25
+ project_id: "Prisma Console → your project → Settings → Project ID"
26
+ assumptions:
27
+ - id: PP1
28
+ verified: true
29
+ text: "The Management API at `https://api.prisma.io/v1` authenticates with a workspace service token as `Authorization: Bearer`; Prisma's guide keeps it in `PRISMA_SERVICE_TOKEN`."
30
+ source: https://www.prisma.io/docs/guides/management-api-basic
31
+ - id: PP2
32
+ verified: true
33
+ text: "Answers are enveloped: one object as `{ data: {…} }`, a list as `{ data: [...], pagination: { nextCursor, hasMore } }`, paged with `?cursor=`."
34
+ source: https://api.prisma.io/v1/doc
35
+ - id: PP3
36
+ verified: true
37
+ text: "`POST /v1/projects/{projectId}/databases` with `{ name, region, source? }` creates a database (201); `source: { type: database, databaseId }` copies an existing one. It answers the database with `id`, `name`, `status` (`provisioning` or `ready`) and `connections[]`."
38
+ source: https://api.prisma.io/v1/doc
39
+ - id: PP4
40
+ verified: true
41
+ text: "`GET /v1/projects/{projectId}/databases` lists the project's databases with `id` and `name`; it has no name filter, so the list is searched page by page."
42
+ source: https://api.prisma.io/v1/doc
43
+ - id: PP5
44
+ verified: false
45
+ text: "Database names are not unique (the project-scoped create documents no 409), so two databases with one name make `find.match` ambiguous, which is refused with PARAM_INVALID rather than guessed. Sponson never creates a second one: it looks the name up before every create."
46
+ source: https://api.prisma.io/v1/doc
47
+ - id: PP6
48
+ verified: true
49
+ text: "`GET /v1/databases/{databaseId}` answers the database (404 when it does not exist); `DELETE` on the same path deletes it (204; 404 when gone; 403 for a project's default database)."
50
+ source: https://api.prisma.io/v1/doc
51
+ - id: PP7
52
+ verified: false
53
+ text: "The connection strings (`connections[0].endpoints.direct.connectionString`, and `.pooled` when there is a pooled endpoint) are in the create answer only: list and get answer the endpoints' host and port without them. So they are once-only outputs (ADR 0018), and a create whose answer was lost leaves dependents to be refused until the database is recreated. Inferred from the schemas; not stated in the docs."
54
+ source: https://api.prisma.io/v1/doc
55
+ - id: PP8
56
+ verified: false
57
+ text: "`connections[0]` is the database's default connection, present in every create answer."
58
+ source: https://api.prisma.io/v1/doc
59
+ - id: PP9
60
+ verified: false
61
+ text: "A database answered with `status: provisioning` accepts connections within seconds; nothing waits for `ready` (the API has no readiness the http adapter can poll), so a dependent deployment that starts at once may see connection errors for that long."
62
+ source: https://api.prisma.io/v1/doc
63
+ - id: PP10
64
+ verified: true
65
+ text: "Connection strings have the form `postgres://USER:PASSWORD@db.prisma.io:5432/?sslmode=require` (direct) and `postgres://USER:PASSWORD@pooled.db.prisma.io:5432/?sslmode=require` (pooled)."
66
+ source: https://www.prisma.io/docs/postgres/database/connecting-to-your-database
67
+ ops:
68
+ database:
69
+ kind: resource
70
+ title: Database
71
+ summary: "An empty Postgres database per scope in a Prisma Postgres project, such as `app-pr-42`. Its connection strings exist only in the create answer, so they are once-only outputs: reference them from the line that needs them (an env var) in the same plan. Located by name; only its existence is compared for drift; destroy deletes it and its data."
72
+ covers: [prisma-postgres-database]
73
+ params:
74
+ project_id:
75
+ type: string
76
+ required: true
77
+ description: The project's id.
78
+ example: proj_cm1a2b3c4d5e6f7g8h9
79
+ name:
80
+ type: string
81
+ required: true
82
+ description: The database's name.
83
+ example: "app-${ctx.scope}"
84
+ region:
85
+ type: string
86
+ default: us-east-1
87
+ enum: [us-east-1, us-west-1, eu-west-3, eu-central-1, ap-northeast-1, ap-southeast-1]
88
+ description: Where it runs.
89
+ http:
90
+ find:
91
+ path: "/projects/{project_id}/databases"
92
+ list_path: /data
93
+ next: /pagination/nextCursor
94
+ match: { name: { param: name } }
95
+ read: { path: "/databases/{id}" }
96
+ create:
97
+ path: "/projects/{project_id}/databases"
98
+ body: { region: { param: region } }
99
+ item_path: /data
100
+ outputs:
101
+ status: /status
102
+ direct_url: { path: /connections/0/endpoints/direct/connectionString, sensitive: true, once: true }
103
+ pooled_url: { path: /connections/0/endpoints/pooled/connectionString, sensitive: true, once: true }
104
+ live:
105
+ params: { project_id: SPONSON_LIVE_PRISMA_PROJECT_ID }
106
+ database_copy:
107
+ kind: resource
108
+ title: Database copied from another
109
+ summary: "A Postgres database per scope copied from an existing one (`source: { type: database }`), such as `app-pr-42` with the data of `app`. Otherwise like `prisma.database`: once-only connection strings, existence-only drift, destroy deletes the copy (never the source)."
110
+ covers: [prisma-postgres-database]
111
+ params:
112
+ project_id:
113
+ type: string
114
+ required: true
115
+ description: The project's id.
116
+ example: proj_cm1a2b3c4d5e6f7g8h9
117
+ name:
118
+ type: string
119
+ required: true
120
+ description: The new database's name.
121
+ example: "app-${ctx.scope}"
122
+ source_database_id:
123
+ type: string
124
+ required: true
125
+ description: The id of the database to copy.
126
+ example: db_cm9z8y7x6w5v4u3t2s1
127
+ region:
128
+ type: string
129
+ default: us-east-1
130
+ enum: [us-east-1, us-west-1, eu-west-3, eu-central-1, ap-northeast-1, ap-southeast-1]
131
+ description: Where it runs.
132
+ http:
133
+ find:
134
+ path: "/projects/{project_id}/databases"
135
+ list_path: /data
136
+ next: /pagination/nextCursor
137
+ match: { name: { param: name } }
138
+ read: { path: "/databases/{id}" }
139
+ create:
140
+ path: "/projects/{project_id}/databases"
141
+ body:
142
+ region: { param: region }
143
+ source: { type: database, databaseId: { param: source_database_id } }
144
+ item_path: /data
145
+ outputs:
146
+ status: /status
147
+ direct_url: { path: /connections/0/endpoints/direct/connectionString, sensitive: true, once: true }
148
+ pooled_url: { path: /connections/0/endpoints/pooled/connectionString, sensitive: true, once: true }
149
+ live:
150
+ params: { project_id: SPONSON_LIVE_PRISMA_PROJECT_ID, source_database_id: SPONSON_LIVE_PRISMA_DATABASE_ID }