@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,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 }
@@ -0,0 +1,134 @@
1
+ # yaml-language-server: $schema=../../../schema/recipe.schema.json
2
+ # Upstash QStash recipes (its own API and token; Upstash Redis is recipes/upstash.yaml).
3
+ # Format and checklist: docs/adr/0020-recipes.md, CONTRIBUTING.md "Adding a recipe".
4
+ provider: qstash
5
+ title: Upstash QStash
6
+ category: queues
7
+ verified_on: "2026-10-11"
8
+ docs:
9
+ - title: Authentication
10
+ url: https://upstash.com/docs/qstash/api/authentication
11
+ - title: Create a schedule
12
+ url: https://upstash.com/docs/qstash/api-reference/schedules/create-a-schedule
13
+ - title: Get a schedule
14
+ url: https://upstash.com/docs/qstash/api-reference/schedules/get-a-schedule
15
+ - title: Delete a schedule
16
+ url: https://upstash.com/docs/qstash/api-reference/schedules/delete-a-schedule
17
+ - title: API examples (raw destination URL in the path)
18
+ url: https://upstash.com/docs/qstash/overall/apiexamples
19
+ - title: Multi-region (base URLs, tokens)
20
+ url: https://upstash.com/docs/qstash/howto/multi-region
21
+ api:
22
+ base_url: https://qstash.upstash.io
23
+ base_url_env: QSTASH_API_URL
24
+ auth: { bearer_env: QSTASH_TOKEN }
25
+ detect:
26
+ deps: ["@upstash/qstash"]
27
+ env: ["QSTASH_*"]
28
+ starter:
29
+ - op: schedule
30
+ values: { schedule_id: "cron-${ctx.scope}" }
31
+ where:
32
+ destination: "the preview's endpoint the schedule calls: its URL plus the path (a `{ from }` reference cannot carry a path)"
33
+ assumptions:
34
+ - id: QS1
35
+ verified: true
36
+ text: "Requests authenticate with the QStash token as `Authorization: Bearer`; the SDKs read it from `QSTASH_TOKEN`."
37
+ source: https://upstash.com/docs/qstash/api/authentication
38
+ - id: QS2
39
+ verified: true
40
+ text: "Each region (`qstash.upstash.io` = EU, `qstash-us-east-1.upstash.io`) has its own token and its own schedules; the default here is the EU URL, and a US account overrides `base_url` in its block."
41
+ source: https://upstash.com/docs/qstash/howto/multi-region
42
+ - id: QS3
43
+ verified: true
44
+ text: "`POST /v2/schedules/{destination}` creates a schedule: the cron in `Upstash-Cron`, the method used to call the destination in `Upstash-Method` (default POST), the request body as the message delivered on every run. It answers `{ scheduleId }`."
45
+ source: https://upstash.com/docs/qstash/api-reference/schedules/create-a-schedule
46
+ - id: QS4
47
+ verified: true
48
+ text: "With `Upstash-Schedule-Id`, a schedule with that id is updated with the new settings if it exists (an upsert): the id is the client's, the read path names it, and the same request is both create and update."
49
+ source: https://upstash.com/docs/qstash/api-reference/schedules/create-a-schedule
50
+ - id: QS5
51
+ verified: true
52
+ text: "The official example writes the destination URL in the path as it is (`/v2/schedules/https://example.com`), hence the `{+destination}` placeholder."
53
+ source: https://upstash.com/docs/qstash/overall/apiexamples
54
+ - id: QS6
55
+ verified: false
56
+ text: "A destination URL with a query string arrives intact with its `?` escaped as `%3F` (a raw `?` would end the path); not documented."
57
+ - id: QS7
58
+ verified: true
59
+ text: "`GET /v2/schedules/{scheduleId}` answers the schedule bare with `scheduleId`, `cron`, `destination`, `method` and `body`, and 404 when there is none; `DELETE` on the same path removes it."
60
+ source: https://upstash.com/docs/qstash/api-reference/schedules/get-a-schedule
61
+ - id: QS8
62
+ verified: false
63
+ text: "Deleting a schedule that is already gone answers 404 (only 200 is documented), which destroy treats as done."
64
+ source: https://upstash.com/docs/qstash/api-reference/schedules/delete-a-schedule
65
+ - id: QS9
66
+ verified: true
67
+ text: "Schedule ids are alphanumerics, hyphens, periods and underscores (400 otherwise); a cron may carry a `CRON_TZ=<zone>` prefix and runs at most once a minute."
68
+ source: https://upstash.com/docs/qstash/api-reference/schedules/create-a-schedule
69
+ - id: QS10
70
+ verified: false
71
+ text: "`GET` returns `cron`, `destination` and `method` exactly as they were sent (the cron string unnormalised), so comparing them is not drift on every plan."
72
+ source: https://upstash.com/docs/qstash/api-reference/schedules/get-a-schedule
73
+ ops:
74
+ schedule:
75
+ kind: resource
76
+ title: Schedule
77
+ summary: "A cron schedule that calls a preview's URL, such as `/api/cron` of `pr-42`, under a schedule id per scope. Created and updated by the same upsert (QS4); a console edit of its cron, destination or method is drift; destroy deletes it. The message body is sent on create and update but not compared."
78
+ covers: [qstash-schedule]
79
+ params:
80
+ schedule_id:
81
+ type: string
82
+ required: true
83
+ pattern: "^[A-Za-z0-9._-]+$"
84
+ description: The schedule's id, chosen by the plan (one per scope).
85
+ example: "cron-${ctx.scope}"
86
+ destination:
87
+ type: string
88
+ required: true
89
+ pattern: "^https?://"
90
+ description: The URL the schedule calls.
91
+ example: "https://${ctx.scope}.preview.example.com/api/cron"
92
+ cron:
93
+ type: string
94
+ required: true
95
+ description: "The cron expression, optionally prefixed `CRON_TZ=<zone> `."
96
+ example: "*/15 * * * *"
97
+ method:
98
+ type: string
99
+ default: POST
100
+ enum: [GET, POST, PUT, PATCH, DELETE]
101
+ description: The HTTP method used to call the destination.
102
+ body:
103
+ type: map
104
+ description: The JSON message delivered on every run (sent, not compared).
105
+ http:
106
+ read: { path: "/v2/schedules/{schedule_id}" }
107
+ create:
108
+ path: "/v2/schedules/{+destination}"
109
+ headers:
110
+ Upstash-Schedule-Id: { param: schedule_id }
111
+ Upstash-Cron: { param: cron }
112
+ Upstash-Method: { param: method }
113
+ send_fields: false
114
+ body: { param: body }
115
+ update:
116
+ method: POST
117
+ path: "/v2/schedules/{+destination}"
118
+ headers:
119
+ Upstash-Schedule-Id: { param: schedule_id }
120
+ Upstash-Cron: { param: cron }
121
+ Upstash-Method: { param: method }
122
+ send_fields: false
123
+ body: { param: body }
124
+ id_path: /scheduleId
125
+ fields:
126
+ cron: { param: cron }
127
+ destination: { param: destination }
128
+ method: { param: method }
129
+ sim:
130
+ collection: /v2/schedules
131
+ header_fields: { upstash-schedule-id: scheduleId, upstash-cron: cron, upstash-method: method }
132
+ suffix_field: destination
133
+ live:
134
+ params: { destination: SPONSON_LIVE_QSTASH_DESTINATION }
@@ -0,0 +1,130 @@
1
+ # yaml-language-server: $schema=../../../schema/recipe.schema.json
2
+ # Railway public GraphQL API recipes. Format and checklist: docs/adr/0020-recipes.md, CONTRIBUTING.md "Adding a recipe".
3
+ provider: railway
4
+ title: Railway
5
+ category: deploy
6
+ verified_on: "2026-10-11"
7
+ docs:
8
+ - title: Public API (endpoint, tokens, errors)
9
+ url: https://docs.railway.com/reference/public-api
10
+ - title: Manage Variables with the Public API
11
+ url: https://docs.railway.com/guides/manage-variables
12
+ - title: Environments (PR environments)
13
+ url: https://docs.railway.com/reference/environments
14
+ - title: CLI (RAILWAY_TOKEN and RAILWAY_API_TOKEN)
15
+ url: https://docs.railway.com/guides/cli
16
+ - title: railwayapp/cli GraphQL schema
17
+ url: https://github.com/railwayapp/cli/blob/master/src/gql/schema.json
18
+ api:
19
+ base_url: https://backboard.railway.com/graphql/v2
20
+ base_url_env: RAILWAY_API_URL
21
+ auth: { bearer_env: RAILWAY_API_TOKEN }
22
+ detect:
23
+ env: ["RAILWAY_*"]
24
+ files: [railway.json, railway.toml]
25
+ starter:
26
+ - op: variable
27
+ where:
28
+ project_id: "Railway → your project → Settings → Project ID, or `railway status --json`"
29
+ environment_id: "the pull request environment Railway created: its Settings, or `railway environment`"
30
+ assumptions:
31
+ - id: RY1
32
+ verified: true
33
+ text: "The public API is one GraphQL endpoint, `https://backboard.railway.com/graphql/v2`; account, workspace and OAuth tokens are sent as `Authorization: Bearer <token>`, project tokens in `Project-Access-Token` instead (a project token therefore needs a hand-written block with `auth: { header: Project-Access-Token, value_env: RAILWAY_TOKEN }`)."
34
+ source: https://docs.railway.com/reference/public-api
35
+ - id: RY2
36
+ verified: true
37
+ text: "The Railway CLI reads an account or workspace token from `RAILWAY_API_TOKEN` and a project token from `RAILWAY_TOKEN`, so the bearer credential of this recipe is `RAILWAY_API_TOKEN`."
38
+ source: https://docs.railway.com/guides/cli
39
+ - id: RY3
40
+ verified: true
41
+ text: "Failures may be HTTP 200 with an `errors` array (a denied query among them); 400 is a parse or validation failure, 429 a rate limit with `Retry-After`. Queries are safe to retry; a mutation only when no answer arrived."
42
+ source: https://docs.railway.com/reference/public-api
43
+ - id: RY4
44
+ verified: true
45
+ text: "`variables(projectId, environmentId, serviceId?, unrendered?)` answers the variables of a service in an environment (without `serviceId`, the environment's shared variables) as a map of name to value; `unrendered: true` answers references such as `${{Postgres.DATABASE_URL}}` as written instead of resolved, so a value is compared as it was set."
46
+ source: https://docs.railway.com/guides/manage-variables
47
+ - id: RY5
48
+ verified: true
49
+ text: "`variableUpsert(input: { projectId, environmentId, serviceId?, name, value, skipDeploys? })` creates or replaces one variable (without `serviceId`, a shared variable); by default it triggers a redeploy of the service (`skipDeploys` defaults to false)."
50
+ source: https://docs.railway.com/guides/manage-variables
51
+ - id: RY6
52
+ verified: true
53
+ text: "`variableDelete(input: { projectId, environmentId, serviceId?, name })` deletes one variable."
54
+ source: https://docs.railway.com/guides/manage-variables
55
+ - id: RY7
56
+ verified: true
57
+ text: "Railway creates a PR environment when a pull request is opened (with PR environments enabled) and deletes it, with its variables, when the pull request is merged or closed. The recipe takes the environment's id as a param: the plan copies it from the dashboard, `railway status`, or a line that outputs it; the recipe does not look environments up by name (their naming is not documented)."
58
+ source: https://docs.railway.com/reference/environments
59
+ - id: RY8
60
+ verified: false
61
+ text: "Deleting a variable whose environment Railway already removed (the pull request closed) is an error saying the environment was not found, which the adapter reads as already gone; deleting a variable that does not exist answers `true`."
62
+ - id: RY9
63
+ verified: false
64
+ text: "Sponson does not wait for the redeploy a write triggers (RY5): a step that needs the running service to see the value waits on Railway's deployment status itself. A reference value written as `${{…}}` is not supported in a plan, where `${…}` is Sponson's own interpolation."
65
+ ops:
66
+ variable:
67
+ kind: resource
68
+ title: Variable of a service in an environment
69
+ summary: "One variable of a service (or, without `service_id`, a shared variable) in a Railway environment, typically the PR environment Railway created. Upserted with `variableUpsert`, read unrendered from `variables`, removed with `variableDelete`. A console edit of the value is drift. Railway redeploys the service after each write unless `skip_deploys` is set; Sponson does not wait for that deploy (RY9)."
70
+ covers: [railway-variables]
71
+ params:
72
+ project_id:
73
+ type: string
74
+ required: true
75
+ description: The project's id.
76
+ example: prj-railway-demo
77
+ environment_id:
78
+ type: string
79
+ required: true
80
+ description: The environment's id (for a PR environment, the one Railway created for the pull request; RY7).
81
+ example: env-pr-42
82
+ service_id:
83
+ type: string
84
+ description: The service's id; without it, the variable is shared by the environment's services.
85
+ example: svc-web
86
+ name:
87
+ type: string
88
+ required: true
89
+ pattern: "^[A-Za-z_][A-Za-z0-9_]*$"
90
+ description: The variable's name.
91
+ example: API_URL
92
+ value:
93
+ type: string
94
+ required: true
95
+ description: The variable's value (may be a `{ secret }` reference).
96
+ example: "https://api-${ctx.scope}.example.app"
97
+ skip_deploys:
98
+ type: boolean
99
+ description: Do not redeploy the service after a write (Railway's default is to redeploy).
100
+ http:
101
+ read:
102
+ graphql:
103
+ query: "query variables($projectId: String!, $environmentId: String!, $serviceId: String, $unrendered: Boolean) { variables(projectId: $projectId, environmentId: $environmentId, serviceId: $serviceId, unrendered: $unrendered) }"
104
+ variables: { projectId: { param: project_id }, environmentId: { param: environment_id }, serviceId: { param: service_id }, unrendered: true }
105
+ data_path: { join: ["/variables/", { param: name }] }
106
+ create:
107
+ graphql:
108
+ query: "mutation variableUpsert($input: VariableUpsertInput!) { variableUpsert(input: $input) }"
109
+ variables: { input: { projectId: { param: project_id }, environmentId: { param: environment_id }, serviceId: { param: service_id }, name: { param: name }, value: { param: value }, skipDeploys: { param: skip_deploys } } }
110
+ idempotent: true
111
+ update:
112
+ graphql:
113
+ query: "mutation variableUpsert($input: VariableUpsertInput!) { variableUpsert(input: $input) }"
114
+ variables: { input: { projectId: { param: project_id }, environmentId: { param: environment_id }, serviceId: { param: service_id }, name: { param: name }, value: { param: value }, skipDeploys: { param: skip_deploys } } }
115
+ idempotent: true
116
+ delete:
117
+ graphql:
118
+ query: "mutation variableDelete($input: VariableDeleteInput!) { variableDelete(input: $input) }"
119
+ variables: { input: { projectId: { param: project_id }, environmentId: { param: environment_id }, serviceId: { param: service_id }, name: { param: name } } }
120
+ fields:
121
+ value: { param: value }
122
+ sim:
123
+ provider: railway
124
+ seed: { environments: { env-pr-42: { projectId: prj-railway-demo, name: pr-42 } } }
125
+ present: /variables/env-pr-42~1svc-web/API_URL
126
+ drift:
127
+ changed: { vars.env-pr-42.svc-web.API_URL: "https://somewhere-else.example" }
128
+ missing: { vars.env-pr-42.svc-web.API_URL: delete }
129
+ live:
130
+ params: { project_id: SPONSON_LIVE_RAILWAY_PROJECT_ID, environment_id: SPONSON_LIVE_RAILWAY_ENVIRONMENT_ID, service_id: SPONSON_LIVE_RAILWAY_SERVICE_ID }
@@ -0,0 +1,103 @@
1
+ # yaml-language-server: $schema=../../../schema/recipe.schema.json
2
+ # Render API recipes. Format and checklist: docs/adr/0020-recipes.md, CONTRIBUTING.md "Adding a recipe".
3
+ provider: render
4
+ title: Render
5
+ category: deploy
6
+ verified_on: "2026-10-11"
7
+ docs:
8
+ - title: Authentication
9
+ url: https://api-docs.render.com/reference/authentication
10
+ - title: Retrieve environment variable
11
+ url: https://api-docs.render.com/reference/retrieve-env-var
12
+ - title: Add or update environment variable
13
+ url: https://api-docs.render.com/reference/update-env-var
14
+ - title: Delete environment variable
15
+ url: https://api-docs.render.com/reference/delete-env-var
16
+ - title: Update environment variables (replace all; deploy behaviour)
17
+ url: https://api-docs.render.com/reference/update-env-vars-for-service
18
+ - title: Service previews
19
+ url: https://render.com/docs/service-previews
20
+ - title: Render CLI (RENDER_API_KEY)
21
+ url: https://render.com/docs/cli
22
+ api:
23
+ base_url: https://api.render.com/v1
24
+ base_url_env: RENDER_API_URL
25
+ auth: { bearer_env: RENDER_API_KEY }
26
+ detect:
27
+ env: ["RENDER_*"]
28
+ files: [render.yaml]
29
+ starter:
30
+ - op: env_var
31
+ where:
32
+ service_id: "Render Dashboard → the preview service: the `srv-…` id in its URL"
33
+ assumptions:
34
+ - id: RE1
35
+ verified: true
36
+ text: "Requests authenticate with an API key as `Authorization: Bearer`; the Render CLI reads it from `RENDER_API_KEY`."
37
+ source: https://api-docs.render.com/reference/authentication
38
+ - id: RE2
39
+ verified: true
40
+ text: "`GET /services/{serviceId}/env-vars/{envVarKey}` answers the variable bare, `{ key, value }`, and 404 when it does not exist."
41
+ source: https://api-docs.render.com/reference/retrieve-env-var
42
+ - id: RE3
43
+ verified: true
44
+ text: "`PUT /services/{serviceId}/env-vars/{envVarKey}` with `{ value }` adds the variable or replaces its value (200, `{ key, value }`): a create whose answer was lost is the same request again, and the variable's name is its identity."
45
+ source: https://api-docs.render.com/reference/update-env-var
46
+ - id: RE4
47
+ verified: true
48
+ text: "`DELETE /services/{serviceId}/env-vars/{envVarKey}` removes the variable (204)."
49
+ source: https://api-docs.render.com/reference/delete-env-var
50
+ - id: RE5
51
+ verified: false
52
+ text: "Deleting a variable that is already gone answers 404 (documented only as \"Unable to find the requested resource\"), which destroy treats as done."
53
+ source: https://api-docs.render.com/reference/delete-env-var
54
+ - id: RE6
55
+ verified: true
56
+ text: "Changing environment variables through the API does not deploy the service, whatever its autoDeploy setting (stated for the replace-all endpoint); a running preview sees a new value only after its next deploy."
57
+ source: https://api-docs.render.com/reference/update-env-vars-for-service
58
+ - id: RE7
59
+ verified: false
60
+ text: "The single-variable PUT does not deploy either (its page is silent; assumed like the replace-all endpoint)."
61
+ - id: RE8
62
+ verified: true
63
+ text: "Render creates service previews and preview environments itself (per pull request, from the service's Previews setting or `render.yaml`); a preview copies the base service's variables once, when it is created. No API looks up the preview of pull request N, so the line takes the service's id."
64
+ source: https://render.com/docs/service-previews
65
+ - id: RE9
66
+ verified: false
67
+ text: "`GET` returns the value in plain text for every variable, including ones generated with `generateValue`, so the value can be compared for drift."
68
+ source: https://api-docs.render.com/reference/retrieve-env-var
69
+ ops:
70
+ env_var:
71
+ kind: resource
72
+ title: Service environment variable
73
+ summary: "One environment variable on a Render service, such as a preview service's `API_URL`, written by name (`PUT` adds or replaces it). A console edit of its value is drift; destroy deletes it. The service's id is a param: Render creates preview services itself, and writing a variable does not deploy (RE6)."
74
+ covers: [render-env]
75
+ params:
76
+ service_id:
77
+ type: string
78
+ required: true
79
+ pattern: "^[a-z]{3}-[a-z0-9]+$"
80
+ description: The service's id (`srv-…`, in the service's dashboard URL and settings).
81
+ example: srv-d0a1b2c3d4e5f6g7h8i9
82
+ key:
83
+ type: string
84
+ required: true
85
+ pattern: "^[A-Za-z_][A-Za-z0-9_]*$"
86
+ description: The variable's name.
87
+ example: API_URL
88
+ value:
89
+ type: string
90
+ required: true
91
+ description: "The variable's value: a literal, `{ from }` or `{ secret }`."
92
+ example: "https://api-${ctx.scope}.example.com"
93
+ http:
94
+ read: { path: "/services/{service_id}/env-vars/{key}" }
95
+ create: { method: PUT, path: "/services/{service_id}/env-vars/{key}" }
96
+ update: { method: PUT }
97
+ id_path: /key
98
+ fields:
99
+ value: { param: value }
100
+ sim:
101
+ collection: "/services/{service_id}/env-vars"
102
+ live:
103
+ params: { service_id: SPONSON_LIVE_RENDER_SERVICE_ID }