@sponson/adapters 0.0.0-stage → 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 (132) hide show
  1. package/LICENSE +202 -0
  2. package/README.md +4 -2
  3. package/dist/algolia.d.ts +6 -0
  4. package/dist/algolia.js +151 -0
  5. package/dist/algolia.js.map +1 -0
  6. package/dist/clerk.d.ts +8 -0
  7. package/dist/clerk.js +107 -0
  8. package/dist/clerk.js.map +1 -0
  9. package/dist/cloudflare.d.ts +25 -0
  10. package/dist/cloudflare.js +310 -0
  11. package/dist/cloudflare.js.map +1 -0
  12. package/dist/common.d.ts +73 -0
  13. package/dist/common.js +140 -0
  14. package/dist/common.js.map +1 -0
  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 +88 -0
  25. package/dist/http-adapter-auth.js +437 -0
  26. package/dist/http-adapter-auth.js.map +1 -0
  27. package/dist/http-adapter-client.d.ts +51 -0
  28. package/dist/http-adapter-client.js +156 -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 +179 -0
  46. package/dist/http.js +352 -0
  47. package/dist/http.js.map +1 -0
  48. package/dist/index.d.ts +28 -0
  49. package/dist/index.js +45 -0
  50. package/dist/index.js.map +1 -0
  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.d.ts +8 -0
  58. package/dist/neon.js +167 -0
  59. package/dist/neon.js.map +1 -0
  60. package/dist/netlify.d.ts +18 -0
  61. package/dist/netlify.js +436 -0
  62. package/dist/netlify.js.map +1 -0
  63. package/dist/planetscale.d.ts +10 -0
  64. package/dist/planetscale.js +311 -0
  65. package/dist/planetscale.js.map +1 -0
  66. package/dist/providers.d.ts +26 -0
  67. package/dist/providers.js +36 -0
  68. package/dist/providers.js.map +1 -0
  69. package/dist/recipes.d.ts +167 -0
  70. package/dist/recipes.js +246 -0
  71. package/dist/recipes.js.map +1 -0
  72. package/dist/secrets.d.ts +72 -0
  73. package/dist/secrets.js +476 -0
  74. package/dist/secrets.js.map +1 -0
  75. package/dist/sigv4.d.ts +56 -0
  76. package/dist/sigv4.js +118 -0
  77. package/dist/sigv4.js.map +1 -0
  78. package/dist/supabase.d.ts +9 -0
  79. package/dist/supabase.js +385 -0
  80. package/dist/supabase.js.map +1 -0
  81. package/dist/vercel.d.ts +8 -0
  82. package/dist/vercel.js +475 -0
  83. package/dist/vercel.js.map +1 -0
  84. package/dist/xata.d.ts +8 -0
  85. package/dist/xata.js +142 -0
  86. package/dist/xata.js.map +1 -0
  87. package/dist/xml.d.ts +25 -0
  88. package/dist/xml.js +211 -0
  89. package/dist/xml.js.map +1 -0
  90. package/package.json +58 -3
  91. package/recipes/amplify.yaml +147 -0
  92. package/recipes/auth0.yaml +178 -0
  93. package/recipes/azure_swa.yaml +116 -0
  94. package/recipes/cloud_run.yaml +97 -0
  95. package/recipes/cloudflare.yaml +108 -0
  96. package/recipes/cockroachdb.yaml +132 -0
  97. package/recipes/configcat.yaml +123 -0
  98. package/recipes/datadog.yaml +136 -0
  99. package/recipes/firebase.yaml +84 -0
  100. package/recipes/flagsmith.yaml +98 -0
  101. package/recipes/fly.yaml +97 -0
  102. package/recipes/fly_machines.yaml +67 -0
  103. package/recipes/gcs.yaml +102 -0
  104. package/recipes/github.yaml +123 -0
  105. package/recipes/growthbook.yaml +124 -0
  106. package/recipes/heroku.yaml +98 -0
  107. package/recipes/honeycomb.yaml +89 -0
  108. package/recipes/inngest.yaml +97 -0
  109. package/recipes/kinde.yaml +120 -0
  110. package/recipes/mongodb_atlas.yaml +119 -0
  111. package/recipes/okta.yaml +177 -0
  112. package/recipes/posthog.yaml +127 -0
  113. package/recipes/postmark.yaml +105 -0
  114. package/recipes/postmark_server.yaml +93 -0
  115. package/recipes/prisma.yaml +150 -0
  116. package/recipes/qstash.yaml +134 -0
  117. package/recipes/railway.yaml +130 -0
  118. package/recipes/render.yaml +103 -0
  119. package/recipes/resend.yaml +130 -0
  120. package/recipes/s3.yaml +108 -0
  121. package/recipes/sendgrid.yaml +165 -0
  122. package/recipes/sentry.yaml +158 -0
  123. package/recipes/split.yaml +121 -0
  124. package/recipes/statsig.yaml +124 -0
  125. package/recipes/stripe.yaml +310 -0
  126. package/recipes/stytch.yaml +113 -0
  127. package/recipes/svix.yaml +109 -0
  128. package/recipes/turso.yaml +98 -0
  129. package/recipes/unleash.yaml +149 -0
  130. package/recipes/upstash.yaml +130 -0
  131. package/recipes/vercel.yaml +116 -0
  132. package/recipes/workos.yaml +70 -0
@@ -0,0 +1,130 @@
1
+ # yaml-language-server: $schema=../../../schema/recipe.schema.json
2
+ # Resend API recipes. Format and checklist: docs/adr/0020-recipes.md, CONTRIBUTING.md "Adding a recipe".
3
+ provider: resend
4
+ title: Resend
5
+ category: email
6
+ verified_on: "2026-10-11"
7
+ docs:
8
+ - title: Create API key
9
+ url: https://resend.com/docs/api-reference/api-keys/create-api-key
10
+ - title: List API keys
11
+ url: https://resend.com/docs/api-reference/api-keys/list-api-keys
12
+ - title: Delete API key
13
+ url: https://resend.com/docs/api-reference/api-keys/delete-api-key
14
+ - title: Create webhook
15
+ url: https://resend.com/docs/api-reference/webhooks/create-webhook
16
+ - title: Retrieve webhook
17
+ url: https://resend.com/docs/api-reference/webhooks/get-webhook
18
+ - title: List webhooks
19
+ url: https://resend.com/docs/api-reference/webhooks/list-webhooks
20
+ - title: Update webhook
21
+ url: https://resend.com/docs/api-reference/webhooks/update-webhook
22
+ - title: Delete webhook
23
+ url: https://resend.com/docs/api-reference/webhooks/delete-webhook
24
+ api:
25
+ base_url: https://api.resend.com
26
+ base_url_env: RESEND_API_URL
27
+ auth: { bearer_env: RESEND_API_KEY }
28
+ detect:
29
+ deps: [resend]
30
+ env: ["RESEND_*"]
31
+ starter:
32
+ - op: api_key
33
+ values: { name: "sponson-${ctx.env}-${ctx.scope}" }
34
+ assumptions:
35
+ - id: RS1
36
+ verified: true
37
+ text: "`POST /api-keys` creates an API key from `name` (at most 50 characters), `permission` (`full_access` or `sending_access`) and `domain_id` (with `sending_access` only), answering `{ id, token }`; requests carry `Authorization: Bearer <key>`."
38
+ source: https://resend.com/docs/api-reference/api-keys/create-api-key
39
+ - id: RS2
40
+ verified: true
41
+ text: "`GET /api-keys` answers `{ object: list, has_more, data: [{ id, name, created_at, last_used_at }] }`, every key when no `limit` is sent, and never the token: the token is shown only in the create answer, so it is a once-only output."
42
+ source: https://resend.com/docs/api-reference/api-keys/list-api-keys
43
+ - id: RS3
44
+ verified: true
45
+ text: "`DELETE /api-keys/{api_key_id}` removes a key; there is no endpoint to read or change one key, so its permission and domain are sent on create and never compared."
46
+ source: https://resend.com/docs/api-reference/api-keys/delete-api-key
47
+ - id: RS4
48
+ verified: true
49
+ text: "`POST /webhooks` creates a webhook from `endpoint` and `events` (both required), answering `{ object, id, signing_secret }`."
50
+ source: https://resend.com/docs/api-reference/webhooks/create-webhook
51
+ - id: RS5
52
+ verified: true
53
+ text: "`GET /webhooks/{webhook_id}` answers the webhook with `status` (`enabled`/`disabled`), `endpoint`, `events` and `signing_secret`: the secret stays readable, so it is an ordinary sensitive output."
54
+ source: https://resend.com/docs/api-reference/webhooks/get-webhook
55
+ - id: RS6
56
+ verified: true
57
+ text: "`GET /webhooks` answers `{ object: list, has_more, data: [{ id, created_at, status, endpoint, events }] }`, every webhook when no `limit` is sent."
58
+ source: https://resend.com/docs/api-reference/webhooks/list-webhooks
59
+ - id: RS7
60
+ verified: true
61
+ text: "`PATCH /webhooks/{webhook_id}` changes `endpoint`, `events` and `status`; `DELETE` on the same path removes the webhook."
62
+ source: https://resend.com/docs/api-reference/webhooks/update-webhook
63
+ - id: RS8
64
+ verified: false
65
+ text: "Key names and webhook endpoints are not unique in Resend: the recipe takes the name (keys) and the endpoint URL (webhooks) as the identity, and refuses (PARAM_INVALID) when two match, so give each environment its own name and URL."
66
+ - id: RS9
67
+ verified: false
68
+ text: "Creating a key or a webhook needs a `full_access` key; a deleted object answers 404."
69
+ ops:
70
+ api_key:
71
+ kind: resource
72
+ title: API key
73
+ summary: "A restricted API key per environment, such as `sponson-preview-pr-42` allowed only to send from one domain. Its token is a once-only output: pass it to the app's environment line in the same run (`{ from: <line>.token }`); later runs keep what that line holds, and `apply --recreate <line>` issues a new one. Only its existence is compared for drift: Resend shows no permission or domain for a key."
74
+ covers: [resend-key-webhook]
75
+ params:
76
+ name:
77
+ type: string
78
+ required: true
79
+ pattern: "^.{1,50}$"
80
+ description: The key's name, unique among the account's keys (RS8); its identity.
81
+ example: "sponson-${ctx.env}-${ctx.scope}"
82
+ permission:
83
+ type: string
84
+ default: sending_access
85
+ enum: [full_access, sending_access]
86
+ description: What the key may do.
87
+ domain_id:
88
+ type: string
89
+ description: With `sending_access`, the only domain the key may send from.
90
+ http:
91
+ find:
92
+ path: /api-keys
93
+ list_path: /data
94
+ match: { name: { param: name } }
95
+ create:
96
+ path: /api-keys
97
+ body: { permission: { param: permission }, domain_id: { param: domain_id } }
98
+ delete: { path: "/api-keys/{id}" }
99
+ outputs:
100
+ token: { path: /token, sensitive: true, once: true }
101
+ webhook:
102
+ kind: resource
103
+ title: Webhook
104
+ summary: "A webhook delivering an environment's email events to its URL, such as the preview's `/api/resend` handler. Located by its endpoint URL; a console edit of its events is drift (its enabled/disabled status is left to the console: the create request takes none). Its signing secret is a sensitive output, readable on every run."
105
+ covers: [resend-key-webhook]
106
+ params:
107
+ endpoint:
108
+ type: string
109
+ required: true
110
+ pattern: "^https://\\S+$"
111
+ description: The URL events are delivered to; its identity (RS8).
112
+ example: "https://${ctx.scope}.preview.example.com/api/resend"
113
+ events:
114
+ type: list
115
+ required: true
116
+ description: The event types to deliver, such as `email.delivered` and `email.bounced`.
117
+ example: [email.delivered, email.bounced]
118
+ http:
119
+ find:
120
+ path: /webhooks
121
+ list_path: /data
122
+ match: { endpoint: { param: endpoint } }
123
+ read: { path: "/webhooks/{id}" }
124
+ create: { path: /webhooks }
125
+ update: { method: PATCH, path: "/webhooks/{id}" }
126
+ delete: { path: "/webhooks/{id}" }
127
+ fields:
128
+ events: { param: events }
129
+ outputs:
130
+ signing_secret: { path: /signing_secret, sensitive: true }
@@ -0,0 +1,108 @@
1
+ # yaml-language-server: $schema=../../../schema/recipe.schema.json
2
+ # S3 API recipes (Amazon S3, and Cloudflare R2 through its S3-compatible endpoint): XML documents, every request
3
+ # signed with AWS Signature Version 4. Format and checklist: docs/adr/0020-recipes.md, CONTRIBUTING.md "Adding a recipe".
4
+ provider: s3
5
+ title: Amazon S3 / Cloudflare R2 (S3 API)
6
+ category: cors
7
+ verified_on: "2026-10-11"
8
+ docs:
9
+ - title: PutBucketCors
10
+ url: https://docs.aws.amazon.com/AmazonS3/latest/API/API_PutBucketCors.html
11
+ - title: GetBucketCors
12
+ url: https://docs.aws.amazon.com/AmazonS3/latest/API/API_GetBucketCors.html
13
+ - title: DeleteBucketCors
14
+ url: https://docs.aws.amazon.com/AmazonS3/latest/API/API_DeleteBucketCors.html
15
+ - title: R2 S3 API compatibility
16
+ url: https://developers.cloudflare.com/r2/api/s3/api/
17
+ base_url_example: https://assets.s3.us-east-1.amazonaws.com
18
+ api:
19
+ base_url_env: S3_API_URL
20
+ encoding: xml
21
+ auth:
22
+ aws_sigv4: { region: us-east-1, service: s3, access_key_id_env: AWS_ACCESS_KEY_ID, secret_access_key_env: AWS_SECRET_ACCESS_KEY }
23
+ detect:
24
+ deps: ["@aws-sdk/client-s3"]
25
+ starter:
26
+ - op: bucket_cors
27
+ preview_url: origin
28
+ values: { rule_id: "sponson-${ctx.scope}" }
29
+ where:
30
+ base_url: "the bucket's endpoint: `https://<bucket>.s3.<region>.amazonaws.com`, or R2's `https://<account id>.r2.cloudflarestorage.com/<bucket>`"
31
+ assumptions:
32
+ - id: SS1
33
+ verified: true
34
+ text: "`PUT /?cors` on the bucket's endpoint replaces its whole CORS configuration with a `CORSConfiguration` document of up to 100 `CORSRule`s (each `ID`, `AllowedOrigin`s, `AllowedMethod`s, `AllowedHeader`s, `ExposeHeader`s, `MaxAgeSeconds`) and requires `Content-MD5`, which the SigV4 signer adds for `s3`."
35
+ source: https://docs.aws.amazon.com/AmazonS3/latest/API/API_PutBucketCors.html
36
+ - id: SS2
37
+ verified: true
38
+ text: "`GET /?cors` answers the configuration as XML."
39
+ source: https://docs.aws.amazon.com/AmazonS3/latest/API/API_GetBucketCors.html
40
+ - id: SS3
41
+ verified: true
42
+ text: "`DELETE /?cors` removes the configuration (the way to leave a bucket with no rule)."
43
+ source: https://docs.aws.amazon.com/AmazonS3/latest/API/API_DeleteBucketCors.html
44
+ - id: SS4
45
+ verified: true
46
+ text: "R2 implements PutBucketCors, GetBucketCors and DeleteBucketCors at `https://<ACCOUNT_ID>.r2.cloudflarestorage.com`; its SigV4 region is `auto`, and `us-east-1` is accepted as an alias, so the recipe's region works for R2 unchanged."
47
+ source: https://developers.cloudflare.com/r2/api/s3/api/
48
+ - id: SS5
49
+ verified: false
50
+ text: "A bucket without a CORS configuration answers 404 `NoSuchCORSConfiguration` (read as an empty list, `parent.if_missing: empty`), and an empty configuration is refused as `MalformedXML` (so the last rule is removed with DELETE, `parent.when_empty: delete`)."
51
+ - id: SS6
52
+ verified: false
53
+ text: "R2 accepts path-style requests (`https://<ACCOUNT_ID>.r2.cloudflarestorage.com/<bucket>/?cors`), so its base URL names the bucket in the path."
54
+ - id: SS7
55
+ verified: false
56
+ text: "S3 answers a rule's elements as they were written; a rule's `ID` identifies it among the bucket's rules (IDs are not required to be unique by S3, so other writers must not reuse the recipe's)."
57
+ ops:
58
+ bucket_cors:
59
+ kind: list_item
60
+ title: CORS rule of a bucket
61
+ summary: "A CORS rule of an S3 or R2 bucket allowing the preview's origin, identified by its `ID` (such as `sponson-pr-42`); every other rule is left as it is. The base URL names the bucket: `https://<bucket>.s3.<region>.amazonaws.com` (set `auth.aws_sigv4.region` to the bucket's region), or `https://<account>.r2.cloudflarestorage.com/<bucket>` for R2. A console edit of the rule is drift; destroy removes only this rule (the whole configuration when it was the last one)."
62
+ covers: [s3-r2-cors]
63
+ params:
64
+ rule_id:
65
+ type: string
66
+ required: true
67
+ pattern: "^.{1,255}$"
68
+ description: The rule's `ID`, at most 255 characters; its identity (SS7).
69
+ example: "sponson-${ctx.scope}"
70
+ origin:
71
+ type: string
72
+ required: true
73
+ pattern: "^(https?://\\S+|\\*)$"
74
+ description: The origin allowed (the preview's URL, without a path).
75
+ example: "https://${ctx.scope}.preview.example.app"
76
+ methods:
77
+ type: list
78
+ default: [GET]
79
+ description: "The methods allowed: `GET`, `PUT`, `POST`, `DELETE`, `HEAD`."
80
+ allowed_headers:
81
+ type: list
82
+ description: "Request headers a preflight may ask for (`*` for any); absent: none."
83
+ expose_headers:
84
+ type: list
85
+ description: Response headers the browser may read; absent, none.
86
+ max_age_seconds:
87
+ type: string
88
+ default: "3000"
89
+ pattern: "^[0-9]+$"
90
+ description: How long a preflight answer may be cached, in seconds (a string, as XML carries it).
91
+ http:
92
+ parent: { path: "/?cors", method: PUT, if_missing: empty, when_empty: delete }
93
+ list_path: /CORSConfiguration/CORSRule
94
+ key_field: ID
95
+ item:
96
+ AllowedHeader: { param: allowed_headers }
97
+ AllowedMethod: { param: methods }
98
+ AllowedOrigin: { param: origin }
99
+ ExposeHeader: { param: expose_headers }
100
+ ID: { param: rule_id }
101
+ MaxAgeSeconds: { param: max_age_seconds }
102
+ sim:
103
+ provider: aws
104
+ base_path: /aws/s3/assets
105
+ seed: { buckets: { assets: { cors: null } } }
106
+ present: /buckets/assets/cors/0
107
+ drift:
108
+ missing: { s3.assets.cors: "remove:sponson-pr-42" }
@@ -0,0 +1,165 @@
1
+ # yaml-language-server: $schema=../../../schema/recipe.schema.json
2
+ # Twilio SendGrid v3 API recipes. Format and checklist: docs/adr/0020-recipes.md, CONTRIBUTING.md "Adding a recipe".
3
+ provider: sendgrid
4
+ title: SendGrid
5
+ category: email
6
+ verified_on: "2026-10-11"
7
+ docs:
8
+ - title: Create an Event Webhook
9
+ url: https://www.twilio.com/docs/sendgrid/api-reference/webhooks/create-an-event-webhook
10
+ - title: Get all Event Webhooks
11
+ url: https://www.twilio.com/docs/sendgrid/api-reference/webhooks/get-all-event-webhooks
12
+ - title: Get an Event Webhook
13
+ url: https://www.twilio.com/docs/sendgrid/api-reference/webhooks/get-an-event-webhook
14
+ - title: Update an Event Webhook
15
+ url: https://www.twilio.com/docs/sendgrid/api-reference/webhooks/update-an-event-webhook
16
+ - title: Delete an Event Webhook
17
+ url: https://www.twilio.com/docs/sendgrid/api-reference/webhooks/delete-an-event-webhook
18
+ - title: Create a parse setting
19
+ url: https://www.twilio.com/docs/sendgrid/api-reference/settings-inbound-parse/create-a-parse-setting
20
+ - title: Retrieve a specific parse setting
21
+ url: https://www.twilio.com/docs/sendgrid/api-reference/settings-inbound-parse/retrieve-a-specific-parse-setting
22
+ - title: Update a parse setting
23
+ url: https://www.twilio.com/docs/sendgrid/api-reference/settings-inbound-parse/update-a-parse-setting
24
+ - title: Delete a parse setting
25
+ url: https://www.twilio.com/docs/sendgrid/api-reference/settings-inbound-parse/delete-a-parse-setting
26
+ api:
27
+ base_url: https://api.sendgrid.com
28
+ base_url_env: SENDGRID_API_URL
29
+ auth: { bearer_env: SENDGRID_API_KEY }
30
+ detect:
31
+ deps: ["@sendgrid/"]
32
+ env: ["SENDGRID_*"]
33
+ starter:
34
+ - op: event_webhook
35
+ where:
36
+ url: "the preview's event handler: its URL plus the handler's path (a `{ from }` reference cannot carry a path)"
37
+ assumptions:
38
+ - id: SG1
39
+ verified: true
40
+ text: "`POST /v3/user/webhooks/event/settings` creates an Event Webhook from `url` (required), `enabled`, one boolean per event type (`processed`, `delivered`, `dropped`, `deferred`, `bounce`, `open`, `click`, `spam_report`, `unsubscribe`, `group_unsubscribe`, `group_resubscribe`) and `friendly_name`, answering 201 with the webhook and its string `id`; requests carry the API key as `Authorization: Bearer`. EU regional subusers use `https://api.eu.sendgrid.com`."
41
+ source: https://www.twilio.com/docs/sendgrid/api-reference/webhooks/create-an-event-webhook
42
+ - id: SG2
43
+ verified: true
44
+ text: "Each URL can be used by only one Event Webhook (a second create answers 400 naming the existing id), so the URL identifies the webhook; the number of webhooks is limited by the plan (400 beyond it)."
45
+ source: https://www.twilio.com/docs/sendgrid/api-reference/webhooks/create-an-event-webhook
46
+ - id: SG3
47
+ verified: true
48
+ text: "`GET /v3/user/webhooks/event/settings/all` answers `{ max_allowed, webhooks: [...] }` with every webhook's settings and `id`."
49
+ source: https://www.twilio.com/docs/sendgrid/api-reference/webhooks/get-all-event-webhooks
50
+ - id: SG4
51
+ verified: true
52
+ text: "`PATCH /v3/user/webhooks/event/settings/{id}` requires `url` and updates the fields sent; `DELETE` on the same path answers 204, and 404 for an unknown id. Without an id these paths act on the oldest webhook, which the recipe never does (its paths always carry the id)."
53
+ source: https://www.twilio.com/docs/sendgrid/api-reference/webhooks/update-an-event-webhook
54
+ - id: SG5
55
+ verified: true
56
+ text: "Signature verification and its public key are set through a separate endpoint and are not part of the webhook's create or update; this recipe does not manage them."
57
+ source: https://www.twilio.com/docs/sendgrid/api-reference/webhooks/create-an-event-webhook
58
+ - id: SG6
59
+ verified: true
60
+ text: "`POST /v3/user/webhooks/parse/settings` creates an Inbound Parse setting from `url`, `hostname`, `spam_check` and `send_raw`, answering 201 with those four fields bare; `GET`, `PATCH` and `DELETE` (204) `/v3/user/webhooks/parse/settings/{hostname}` read, update and remove it. The hostname must be on a domain authenticated in SendGrid, with an MX record pointing at SendGrid."
61
+ source: https://www.twilio.com/docs/sendgrid/api-reference/settings-inbound-parse/create-a-parse-setting
62
+ - id: SG7
63
+ verified: false
64
+ text: "A PATCH that leaves out an event flag keeps its current value (the documentation's examples send only `url`); the recipe sends every flag it compares anyway."
65
+ - id: SG8
66
+ verified: false
67
+ text: "A parse setting for a hostname that exists already is refused with an error status (not documented which); the next run then finds it by its hostname."
68
+ ops:
69
+ event_webhook:
70
+ kind: resource
71
+ title: Event Webhook
72
+ summary: An Event Webhook delivering an environment's email events to its URL, such as the preview's `/api/sendgrid/events` handler. Located by its URL (unique in SendGrid); a console edit of its enabled flag, an event flag or its friendly name is drift.
73
+ covers: [sendgrid-webhook]
74
+ params:
75
+ url:
76
+ type: string
77
+ required: true
78
+ pattern: "^https?://\\S+$"
79
+ description: The URL events are posted to; its identity (SG2).
80
+ example: "https://${ctx.scope}.preview.example.com/api/sendgrid/events"
81
+ enabled:
82
+ type: boolean
83
+ default: true
84
+ description: Whether events are posted.
85
+ friendly_name:
86
+ type: string
87
+ description: A label for the webhook (not unique).
88
+ example: "sponson ${ctx.env} ${ctx.scope}"
89
+ processed: { type: boolean, default: false, description: Post `processed` events. }
90
+ delivered: { type: boolean, default: true, description: Post `delivered` events. }
91
+ dropped: { type: boolean, default: true, description: Post `dropped` events. }
92
+ deferred: { type: boolean, default: false, description: Post `deferred` events. }
93
+ bounce: { type: boolean, default: true, description: Post `bounce` events. }
94
+ open: { type: boolean, default: false, description: Post `open` events. }
95
+ click: { type: boolean, default: false, description: Post `click` events. }
96
+ spam_report: { type: boolean, default: true, description: Post `spam_report` events. }
97
+ unsubscribe: { type: boolean, default: false, description: Post `unsubscribe` events. }
98
+ group_unsubscribe: { type: boolean, default: false, description: Post `group_unsubscribe` events. }
99
+ group_resubscribe: { type: boolean, default: false, description: Post `group_resubscribe` events. }
100
+ http:
101
+ find:
102
+ path: /v3/user/webhooks/event/settings/all
103
+ list_path: /webhooks
104
+ match: { url: { param: url } }
105
+ create: { path: /v3/user/webhooks/event/settings }
106
+ update:
107
+ method: PATCH
108
+ path: "/v3/user/webhooks/event/settings/{id}"
109
+ body: { url: { param: url } }
110
+ delete: { path: "/v3/user/webhooks/event/settings/{id}" }
111
+ fields:
112
+ enabled: { param: enabled }
113
+ friendly_name: { param: friendly_name }
114
+ processed: { param: processed }
115
+ delivered: { param: delivered }
116
+ dropped: { param: dropped }
117
+ deferred: { param: deferred }
118
+ bounce: { param: bounce }
119
+ open: { param: open }
120
+ click: { param: click }
121
+ spam_report: { param: spam_report }
122
+ unsubscribe: { param: unsubscribe }
123
+ group_unsubscribe: { param: group_unsubscribe }
124
+ group_resubscribe: { param: group_resubscribe }
125
+ parse_setting:
126
+ kind: resource
127
+ title: Inbound Parse setting
128
+ summary: An Inbound Parse host per environment, such as `pr-42.parse.example.com`, posting the mail it receives to the environment's URL. Named by its hostname; a console edit of its URL, spam check or raw flag is drift. The hostname's domain must be authenticated in SendGrid and its MX record point at SendGrid (a wildcard record serves every environment).
129
+ covers: [sendgrid-webhook]
130
+ params:
131
+ hostname:
132
+ type: string
133
+ required: true
134
+ pattern: "^[a-z0-9.-]+$"
135
+ description: The host mail is received for, lower-case; its identity.
136
+ example: "${ctx.scope}.parse.example.com"
137
+ url:
138
+ type: string
139
+ required: true
140
+ pattern: "^https?://\\S+$"
141
+ description: The URL parsed mail is posted to.
142
+ example: "https://${ctx.scope}.preview.example.com/api/sendgrid/inbound"
143
+ spam_check:
144
+ type: boolean
145
+ default: false
146
+ description: Check mail for spam before posting it.
147
+ send_raw:
148
+ type: boolean
149
+ default: false
150
+ description: Post the original MIME message.
151
+ http:
152
+ read: { path: "/v3/user/webhooks/parse/settings/{hostname}" }
153
+ create:
154
+ path: /v3/user/webhooks/parse/settings
155
+ body: { hostname: { param: hostname } }
156
+ update: { method: PATCH }
157
+ id_path: /hostname
158
+ fields:
159
+ url: { param: url }
160
+ spam_check: { param: spam_check }
161
+ send_raw: { param: send_raw }
162
+ sim:
163
+ id_from: hostname
164
+ live:
165
+ params: { hostname: SPONSON_LIVE_SENDGRID_PARSE_HOSTNAME }
@@ -0,0 +1,158 @@
1
+ # yaml-language-server: $schema=../../../schema/recipe.schema.json
2
+ # Sentry Web API recipes. Format and checklist: docs/adr/0020-recipes.md, CONTRIBUTING.md "Adding a recipe".
3
+ provider: sentry
4
+ title: Sentry
5
+ category: observability
6
+ verified_on: "2026-10-11"
7
+ docs:
8
+ - title: Create a New Release for an Organization
9
+ url: https://docs.sentry.io/api/releases/create-a-new-release-for-an-organization/
10
+ - title: Retrieve an Organization's Release
11
+ url: https://docs.sentry.io/api/releases/retrieve-an-organizations-release/
12
+ - title: Update an Organization's Release
13
+ url: https://docs.sentry.io/api/releases/update-an-organizations-release/
14
+ - title: Delete an Organization's Release
15
+ url: https://docs.sentry.io/api/releases/delete-an-organizations-release/
16
+ - title: Create a Deploy
17
+ url: https://docs.sentry.io/api/releases/create-a-deploy/
18
+ - title: List a Release's Deploys
19
+ url: https://docs.sentry.io/api/releases/list-a-releases-deploys/
20
+ - title: Release Management (sentry-cli)
21
+ url: https://docs.sentry.io/cli/releases/
22
+ api:
23
+ base_url: https://sentry.io/api/0
24
+ base_url_env: SENTRY_API_URL
25
+ auth: { bearer_env: SENTRY_AUTH_TOKEN }
26
+ detect:
27
+ deps: ["@sentry/"]
28
+ env: ["SENTRY_*"]
29
+ files: [sentry.client.config.ts, sentry.client.config.js, sentry.server.config.ts, sentry.server.config.js, sentry.properties, .sentryclirc]
30
+ starter:
31
+ - op: release
32
+ values: { version: "app@${ctx.git.sha}" }
33
+ where:
34
+ organization: "Sentry → Settings → Organization Settings → Organization Slug"
35
+ project: "Sentry → Settings → Projects: the project's slug"
36
+ assumptions:
37
+ - id: SE1
38
+ verified: true
39
+ text: "`POST /api/0/organizations/{organization_id_or_slug}/releases/` creates a release from `version` and `projects` (both required), `ref`, `url`, `dateReleased` and commit data, with a bearer token holding `project:releases`, `project:write`, `project:admin` or `org:ci`; it answers 201 (or 208)."
40
+ source: https://docs.sentry.io/api/releases/create-a-new-release-for-an-organization/
41
+ - id: SE2
42
+ verified: true
43
+ text: "A version is one release across the organization's projects, and `GET /api/0/organizations/{org}/releases/{version}/` answers it (404 when there is none), so the version names the release."
44
+ source: https://docs.sentry.io/api/releases/retrieve-an-organizations-release/
45
+ - id: SE3
46
+ verified: true
47
+ text: "`PUT /api/0/organizations/{org}/releases/{version}/` updates `ref`, `url`, `dateReleased` and commit data (the documentation says an omitted `dateReleased` is the current time), and answers the release."
48
+ source: https://docs.sentry.io/api/releases/update-an-organizations-release/
49
+ - id: SE4
50
+ verified: true
51
+ text: "`DELETE /api/0/organizations/{org}/releases/{version}/` permanently removes a release and its files (`project:releases` or `project:admin`). The recipe keeps releases on destroy: one version is shared by every environment it is deployed to."
52
+ source: https://docs.sentry.io/api/releases/delete-an-organizations-release/
53
+ - id: SE5
54
+ verified: true
55
+ text: "`POST /api/0/organizations/{org}/releases/{version}/deploys/` records a deploy of the release from `environment` (required), `name`, `url`, `dateStarted`, `dateFinished` (default now) and `projects` (default: all of the release's)."
56
+ source: https://docs.sentry.io/api/releases/create-a-deploy/
57
+ - id: SE6
58
+ verified: true
59
+ text: "`GET /api/0/organizations/{org}/releases/{version}/deploys/` lists the release's deploys; deploys cannot be deleted, so the deploy op never deletes (`destroy: never`)."
60
+ source: https://docs.sentry.io/cli/releases/
61
+ - id: SE7
62
+ verified: true
63
+ text: "The release's `dateReleased` defaults to the current time on create, so a release this recipe creates is finalized at once."
64
+ source: https://docs.sentry.io/api/releases/create-a-new-release-for-an-organization/
65
+ - id: SE8
66
+ verified: false
67
+ text: "A create for a version that exists answers 208 with the existing release (not an error), so a release another tool made (sentry-cli in the build) is taken over without a second write; the recipe keeps it on destroy either way."
68
+ - id: SE9
69
+ verified: false
70
+ text: "The deploy list answers a bare array of `{ id, environment, name, url, dateStarted, dateFinished }` with string ids, all of a release's deploys on the first page; `environment` and `name` together identify the deploy this recipe recorded."
71
+ - id: SE10
72
+ verified: false
73
+ text: "Release `url` is answered as sent (null when unset); organizations hosted in another region (`de.sentry.io`) or self-hosted Sentry set `base_url` in the provider block."
74
+ ops:
75
+ release:
76
+ kind: resource
77
+ title: Release
78
+ summary: "A release for the commit, such as `app@${ctx.git.sha}`, in the given projects, finalized when created (SE7). Named by its version; a console edit of its URL is drift. Kept on destroy (shared history: preview and production deploy the same version); `destroy: delete` on the line removes it."
79
+ covers: [sentry-release]
80
+ destroy: keep
81
+ params:
82
+ organization:
83
+ type: string
84
+ required: true
85
+ description: The organization's slug or id.
86
+ example: acme
87
+ version:
88
+ type: string
89
+ required: true
90
+ pattern: "^[^/\\s]+$"
91
+ description: The release's version (no slashes or whitespace), such as `app@<sha>`.
92
+ example: "app@${ctx.git.sha}"
93
+ project:
94
+ type: string
95
+ required: true
96
+ description: The slug of the project the release belongs to (a release of several projects is a hand-written `http` line).
97
+ example: web
98
+ url:
99
+ type: string
100
+ description: A link for the release (its commit or build).
101
+ example: "https://github.com/acme/app/commit/${ctx.git.sha}"
102
+ ref:
103
+ type: string
104
+ description: A commit reference, sent on create.
105
+ http:
106
+ read: { path: "/organizations/{organization}/releases/{version}/" }
107
+ create:
108
+ path: "/organizations/{organization}/releases/"
109
+ body: { version: { param: version }, projects: [{ param: project }], ref: { param: ref } }
110
+ update: { method: PUT }
111
+ id_path: /version
112
+ fields:
113
+ url: { param: url }
114
+ sim:
115
+ id_from: version
116
+ live:
117
+ params: { organization: SPONSON_LIVE_SENTRY_ORG, project: SPONSON_LIVE_SENTRY_PROJECT }
118
+ deploy:
119
+ kind: resource
120
+ title: Deploy of a release to an environment
121
+ summary: "The record that a release was deployed to an environment (`preview`, named for the scope), which Sentry shows on the release and uses for deploy notifications. Located by environment and name among the release's deploys. Sentry has no way to change or delete a deploy, so nothing is compared for drift and destroy leaves it (`destroy: never`)."
122
+ covers: [sentry-release]
123
+ destroy: never
124
+ params:
125
+ organization:
126
+ type: string
127
+ required: true
128
+ description: The organization's slug or id.
129
+ example: acme
130
+ version:
131
+ type: string
132
+ required: true
133
+ pattern: "^[^/\\s]+$"
134
+ description: "The deployed release's version; the release must exist (`{ from: <release line>.id }` orders the lines)."
135
+ example: "app@${ctx.git.sha}"
136
+ environment:
137
+ type: string
138
+ required: true
139
+ description: The environment deployed to.
140
+ example: "${ctx.env}"
141
+ name:
142
+ type: string
143
+ required: true
144
+ description: The deploy's name, which with the environment identifies it among the release's deploys.
145
+ example: "sponson-${ctx.scope}"
146
+ url:
147
+ type: string
148
+ description: A link to the deployment (the preview URL), sent on create.
149
+ http:
150
+ find:
151
+ path: "/organizations/{organization}/releases/{version}/deploys/"
152
+ match: { environment: { param: environment }, name: { param: name } }
153
+ create:
154
+ path: "/organizations/{organization}/releases/{version}/deploys/"
155
+ body: { url: { param: url } }
156
+ delete: { path: "/organizations/{organization}/releases/{version}/deploys/{id}/" }
157
+ live:
158
+ params: { organization: SPONSON_LIVE_SENTRY_ORG, version: SPONSON_LIVE_SENTRY_VERSION }