@substrio/sdk 0.0.0-stage → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +177 -0
- package/README.md +39 -2
- package/package.json +52 -5
- package/substrio.d.ts +1087 -0
- package/substrio.mjs +3928 -0
- package/substrio_errors.mjs +345 -0
- package/substrio_kv_tcp.mjs +559 -0
- package/substrio_operation_client.d.mts +28 -0
- package/substrio_operation_client.mjs +196 -0
- package/substrio_operations.d.mts +11998 -0
- package/substrio_operations.mjs +1731 -0
|
@@ -0,0 +1,1731 @@
|
|
|
1
|
+
// Generated by packages/substrio-sdk/scripts/gen_operations.mjs from the server's operation catalog
|
|
2
|
+
// (GET /v1/_openapi, registry version 1, 548 operations with routes);
|
|
3
|
+
// never edit by hand. Regenerate: npm run gen:operations (packages/substrio-sdk).
|
|
4
|
+
|
|
5
|
+
const route = (method, path, params, query, body, bodyProperty = null) =>
|
|
6
|
+
Object.freeze({ method, path, params: Object.freeze(params), query: Object.freeze(query), body, bodyProperty });
|
|
7
|
+
const operation = (id, name, summary, routes) =>
|
|
8
|
+
Object.freeze({ id, name, words: Object.freeze(name.split('.')), summary, routes: Object.freeze(routes) });
|
|
9
|
+
|
|
10
|
+
/** The registry version the catalog was read at. */
|
|
11
|
+
export const REGISTRY_VERSION = 1;
|
|
12
|
+
|
|
13
|
+
/** Every operation with an HTTP route, by name. */
|
|
14
|
+
export const OPERATIONS = Object.freeze([
|
|
15
|
+
operation(438, "account.app.attach", "Make an existing app one of an account's apps (platform admin)", [
|
|
16
|
+
route("PUT", "/v1/_tenants/{slug}/account", ["slug"], [], true),
|
|
17
|
+
]),
|
|
18
|
+
operation(377, "account.app.create", "Create an app owned by the account. A region (a slug region.choices lists) places it there; one that is not listed is refused with REGION_UNAVAILABLE", [
|
|
19
|
+
route("POST", "/v1/_accounts/{id}/apps", ["id"], [], true),
|
|
20
|
+
]),
|
|
21
|
+
operation(378, "account.app.delete", "Delete one of the account's apps and its data", [
|
|
22
|
+
route("DELETE", "/v1/_accounts/{id}/apps/{slug}", ["id","slug"], [], false),
|
|
23
|
+
]),
|
|
24
|
+
operation(376, "account.app.list", "List the apps an account owns", [
|
|
25
|
+
route("GET", "/v1/_accounts/{id}/apps", ["id"], ["limit","cursor"], false),
|
|
26
|
+
]),
|
|
27
|
+
operation(444, "account.app.open.get", "Read whether other accounts' keys may be scoped to one of the account's apps", [
|
|
28
|
+
route("GET", "/v1/_accounts/{id}/apps/{slug}/keys-open", ["id","slug"], [], false),
|
|
29
|
+
]),
|
|
30
|
+
operation(445, "account.app.open.set", "Open one of the account's apps to other accounts' keys, or close it", [
|
|
31
|
+
route("PUT", "/v1/_accounts/{id}/apps/{slug}/keys-open", ["id","slug"], [], true),
|
|
32
|
+
]),
|
|
33
|
+
operation(789, "account.apps.order", "Set the order the account's apps run in: apps lists every app id of the account once (any other list is refused, INVALID_ARGUMENT naming apps). The first max_apps run; the rest are paused, keeping every byte, and their requests answer APP_OVER_PLAN until they move up, an app is deleted or the plan allows more. Answers each app in order with active, and max_apps (0: no limit)", [
|
|
34
|
+
route("PUT", "/v1/_accounts/{id}/apps/order", ["id"], [], true),
|
|
35
|
+
]),
|
|
36
|
+
operation(562, "account.audit.list", "Read an account's audit trail (its keys, members, invites, apps and clients), newest first; the filters, limit and cursor of audit.list", [
|
|
37
|
+
route("GET", "/v1/_accounts/{id}/audit", ["id"], ["order","since","limit","cursor","until","action","actor_kind","actor","target_kind","target","outcome"], false),
|
|
38
|
+
]),
|
|
39
|
+
operation(743, "account.console.link", "Mint the person you act for a one-time console sign-in, then open its url in their browser: it lands them signed in on their dashboard. Only a person mints one (an agent session for its own person, or the person signed in to the console); a key, the master key or an agent token is refused CONSOLE_LINK_NEEDS_PERSON. The link lives 5 minutes (the server's --console-link-seconds), signs in once, and a new one replaces the last; its token rides in the url's fragment, so no server receives or logs it. Answers {url, expires_at}", [
|
|
40
|
+
route("POST", "/v1/_accounts/console/link", [], [], false),
|
|
41
|
+
]),
|
|
42
|
+
operation(366, "account.create", "Create an account owned by the signed-in person", [
|
|
43
|
+
route("POST", "/v1/_accounts", [], [], true),
|
|
44
|
+
]),
|
|
45
|
+
operation(369, "account.delete", "Delete an account that owns no apps, with its members and keys", [
|
|
46
|
+
route("DELETE", "/v1/_accounts/{id}", ["id"], ["with_apps"], false),
|
|
47
|
+
]),
|
|
48
|
+
operation(367, "account.get", "Read an account", [
|
|
49
|
+
route("GET", "/v1/_accounts/{id}", ["id"], [], false),
|
|
50
|
+
]),
|
|
51
|
+
operation(434, "account.invite.accept", "Accept an invite as the signed-in person and join its account", [
|
|
52
|
+
route("POST", "/v1/_accounts/invites/accept", [], [], true),
|
|
53
|
+
]),
|
|
54
|
+
operation(431, "account.invite.create", "Invite someone to join an account with a role; the link is returned once", [
|
|
55
|
+
route("POST", "/v1/_accounts/{id}/invites", ["id"], [], true),
|
|
56
|
+
]),
|
|
57
|
+
operation(432, "account.invite.list", "List an account's open and expired invites", [
|
|
58
|
+
route("GET", "/v1/_accounts/{id}/invites", ["id"], [], false),
|
|
59
|
+
]),
|
|
60
|
+
operation(433, "account.invite.revoke", "Revoke an invite; its link stops working at once", [
|
|
61
|
+
route("DELETE", "/v1/_accounts/{id}/invites/{invite_id}", ["id","invite_id"], [], false),
|
|
62
|
+
]),
|
|
63
|
+
operation(472, "account.invite_page", "Read the page an invited person joins an account on (text/html)", [
|
|
64
|
+
route("GET", "/oidc/invite", [], [], false),
|
|
65
|
+
]),
|
|
66
|
+
operation(373, "account.key.create", "Create an account key (live or test); the secret is returned once", [
|
|
67
|
+
route("POST", "/v1/_accounts/{id}/keys", ["id"], [], true),
|
|
68
|
+
]),
|
|
69
|
+
operation(374, "account.key.list", "List an account's keys", [
|
|
70
|
+
route("GET", "/v1/_accounts/{id}/keys", ["id"], ["limit","cursor"], false),
|
|
71
|
+
]),
|
|
72
|
+
operation(671, "account.key.publish", "Create a publishable account key, safe in a web page's source: it answers only a browser request from one of its origins, at its apps; the secret is returned once", [
|
|
73
|
+
route("POST", "/v1/_accounts/{id}/publishable-keys", ["id"], [], true),
|
|
74
|
+
]),
|
|
75
|
+
operation(375, "account.key.revoke", "Revoke an account key; it stops working at once", [
|
|
76
|
+
route("DELETE", "/v1/_accounts/{id}/keys/{key_id}", ["id","key_id"], [], false),
|
|
77
|
+
]),
|
|
78
|
+
operation(575, "account.key.update", "Set or clear an account key's own requests_per_minute and request_budget, by key id; a field left out keeps its current value", [
|
|
79
|
+
route("PATCH", "/v1/_accounts/{id}/keys/{key_id}", ["id","key_id"], [], true),
|
|
80
|
+
]),
|
|
81
|
+
operation(365, "account.list", "List the accounts the caller belongs to (every account for the system key)", [
|
|
82
|
+
route("GET", "/v1/_accounts", [], [], false),
|
|
83
|
+
]),
|
|
84
|
+
operation(370, "account.member.list", "List an account's members and their roles", [
|
|
85
|
+
route("GET", "/v1/_accounts/{id}/members", ["id"], [], false),
|
|
86
|
+
]),
|
|
87
|
+
operation(372, "account.member.remove", "Remove a person from an account", [
|
|
88
|
+
route("DELETE", "/v1/_accounts/{id}/members/{user_id}", ["id","user_id"], [], false),
|
|
89
|
+
]),
|
|
90
|
+
operation(371, "account.member.set", "Add a person to an account, or change their role", [
|
|
91
|
+
route("PUT", "/v1/_accounts/{id}/members/{user_id}", ["id","user_id"], [], true),
|
|
92
|
+
]),
|
|
93
|
+
operation(423, "account.mfa.set", "Require every member of an account to sign in with 2FA, or stop requiring it", [
|
|
94
|
+
route("PUT", "/v1/_accounts/{id}/mfa", ["id"], [], true),
|
|
95
|
+
]),
|
|
96
|
+
operation(621, "account.overview", "Read every app of the account with slug, tenant_id, suspended, live version and calls (invocations, status classes, p95_under_ms, from memory); stored_bytes and documents are present only when open is true (the app's database was already open: never opened here to total it), then the account's totals (apps, suspended, open, calls, stored_bytes, documents) across every app", [
|
|
97
|
+
route("GET", "/v1/_accounts/{id}/overview", ["id"], [], false),
|
|
98
|
+
]),
|
|
99
|
+
operation(657, "account.plan.change", "Move the account to a free plan at once when its usage fits (else refused with the limit and by how much). The Substrio plan's items leave the account's subscription first (prorated); the subscription ends when they were all it paid for. A paid plan is bought through account.plan.checkout", [
|
|
100
|
+
route("PUT", "/v1/_accounts/{id}/plan", ["id"], [], true),
|
|
101
|
+
]),
|
|
102
|
+
operation(656, "account.plan.checkout", "Make a cart of a paid Substrio plan and return its checkout_url (and cart) to hand to the person, who pays on that page (billing.checkout.start). Nothing is sent to the provider and nothing changes until the payment is confirmed; the agent never handles payment details", [
|
|
103
|
+
route("POST", "/v1/_accounts/{id}/plan/checkout", ["id"], [], true),
|
|
104
|
+
]),
|
|
105
|
+
operation(655, "account.plan.summary", "Read what the account's plan allows beside what it uses, and every plan with what it adds and its price. An agent explains the plans and the upgrade from this alone", [
|
|
106
|
+
route("GET", "/v1/_accounts/{id}/plan", ["id"], [], false),
|
|
107
|
+
]),
|
|
108
|
+
operation(787, "account.storage", "Read what each app of the account holds on disk (disk_bytes: its database, files, logs, backups, code and the rest, as of its last fold, open or not), the account's total (its apps and their sandboxes, the figure its space is held against) and the space in force (null while none). Supersedes account.overview's stored_bytes, which counts only open apps' document bytes", [
|
|
109
|
+
route("GET", "/v1/_accounts/{id}/storage", ["id"], [], false),
|
|
110
|
+
]),
|
|
111
|
+
operation(368, "account.update", "Rename an account; the system key also sets its services, space and max_apps, and suspends it", [
|
|
112
|
+
route("PATCH", "/v1/_accounts/{id}", ["id"], [], true),
|
|
113
|
+
]),
|
|
114
|
+
operation(446, "account.usage", "Read the account's plan and usage counters at each app it has a plan or usage at", [
|
|
115
|
+
route("GET", "/v1/_accounts/{id}/usage", ["id"], [], false),
|
|
116
|
+
]),
|
|
117
|
+
operation(745, "account.usage.cpu", "Read the CPU the account's apps used in each of the last 60 finished minutes, the hour's mean, the last 15 minutes' mean, the peak minute, the plan's CPU slices and the hour against them (used, of_share), the requests and calls held back while the server was saturated (holds) and each app's minutes and holds, each in CPU slices (1 is the Free plan's CPU under load; Standard holds 4). Explains how close the account runs to its plan's share and who waited for it", [
|
|
118
|
+
route("GET", "/v1/_accounts/{id}/usage/cpu", ["id"], [], false),
|
|
119
|
+
]),
|
|
120
|
+
operation(535, "agent.alert.list", "List the account's agent alerts (a spent key used again revokes its session and leaves one), newest first", [
|
|
121
|
+
route("GET", "/v1/_accounts/{id}/agent/alerts", ["id"], [], false),
|
|
122
|
+
]),
|
|
123
|
+
operation(536, "agent.approval.approve", "Approve one irreversible call an agent session asked for (people only, never an agent); the agent then repeats that exact call with the approval id, once", [
|
|
124
|
+
route("POST", "/v1/_accounts/{id}/agent/approvals/{approval_id}/approve", ["id","approval_id"], [], false),
|
|
125
|
+
]),
|
|
126
|
+
operation(538, "agent.approval.deny", "Deny one irreversible call an agent session asked for (people only); the agent's repeat of it is refused", [
|
|
127
|
+
route("POST", "/v1/_accounts/{id}/agent/approvals/{approval_id}/deny", ["id","approval_id"], [], false),
|
|
128
|
+
]),
|
|
129
|
+
operation(537, "agent.approval.list", "List the irreversible calls of the account's agent sessions that wait for a person's approval, oldest first, a page at a time (limit, cursor; next_cursor), or only those on one app (app) (people only)", [
|
|
130
|
+
route("GET", "/v1/_accounts/{id}/agent/approvals", ["id"], ["app","limit","cursor"], false),
|
|
131
|
+
]),
|
|
132
|
+
operation(530, "agent.enroll.create", "Make a one-time code that an AI agent exchanges for a session acting as you in the account; the code is returned once", [
|
|
133
|
+
route("POST", "/v1/_accounts/{id}/agent/enrollments", ["id"], [], true),
|
|
134
|
+
]),
|
|
135
|
+
operation(531, "agent.enroll.exchange", "Exchange a one-time enrollment code (substrio_ae_...) for an agent session acting as the member who made it: a bearer one unless a kind is named or a public key is sent (key-bound). Answers the credential once and next, what to do with it; the code is spent. Over MCP it is the tool substrio_connect on any /mcp, with no credential", [
|
|
136
|
+
route("POST", "/v1/_agent/exchange", [], [], true),
|
|
137
|
+
]),
|
|
138
|
+
operation(539, "agent.session.delegate", "An agent session makes a narrower child session for a sub-agent: the child's capabilities and apps are within the caller's, it ends no later than the caller, and it dies with the caller. Returns the child's credentials once", [
|
|
139
|
+
route("POST", "/v1/_accounts/{id}/agent/delegations", ["id"], [], true),
|
|
140
|
+
]),
|
|
141
|
+
operation(532, "agent.session.list", "List the account's live agent sessions (an ended one leaves the list; its log is still read by id), oldest first, a page at a time (limit, cursor; next_cursor), or only those reaching one app (app); never a key", [
|
|
142
|
+
route("GET", "/v1/_accounts/{id}/agent/sessions", ["id"], ["app","limit","cursor"], false),
|
|
143
|
+
]),
|
|
144
|
+
operation(540, "agent.session.log", "List the requests an agent session made, oldest first, each with its operation, outcome (ran, refused, approval asked, approved, denied, spent), status, app and time; pages by limit and cursor (a member sees their own agents, an admin or owner the account's). Kept for agent_log_retention_seconds", [
|
|
145
|
+
route("GET", "/v1/_accounts/{id}/agent/sessions/{session_id}/log", ["id","session_id"], ["limit","cursor"], false),
|
|
146
|
+
]),
|
|
147
|
+
operation(533, "agent.session.revoke", "Revoke one agent session (and every session it made); its keys fail at once", [
|
|
148
|
+
route("DELETE", "/v1/_accounts/{id}/agent/sessions/{session_id}", ["id","session_id"], [], false),
|
|
149
|
+
]),
|
|
150
|
+
operation(534, "agent.session.revoke_all", "Revoke every agent session of the account; their keys fail at once", [
|
|
151
|
+
route("DELETE", "/v1/_accounts/{id}/agent/sessions", ["id"], [], false),
|
|
152
|
+
]),
|
|
153
|
+
operation(653, "agent.signup.finish", "Prove the email with the mailed code and get an agent session acting as that person in account_id, else their personal account (one is made for a person with none): the person is made when nobody holds the address. Answers {account_id, account_created, person_created, user_id, account, app_defaults, session (credentials, shown once), next_steps}; a refusal names its reason: a code that proves nothing as auth.email.code_verify (MISSING, USED, EXPIRED, TOO_MANY_ATTEMPTS, WRONG and attempts_remaining), then MFA_REQUIRED, ACCOUNT_REQUIRED (with account_ids), NOT_MEMBER, PERSON_DISABLED, ACCOUNT_SUSPENDED, ACCOUNT_LIMIT", [
|
|
154
|
+
route("POST", "/v1/_agent/signup/finish", [], [], true),
|
|
155
|
+
]),
|
|
156
|
+
operation(652, "agent.signup.start", "Start here with no key: mail a sign-up or sign-in code to the person's email (the identity app's auth.email.code_request). Answers {accepted, expires_at} whether or not anyone holds the address; then agent.signup.finish with the code", [
|
|
157
|
+
route("POST", "/v1/_agent/signup/start", [], [], true),
|
|
158
|
+
]),
|
|
159
|
+
operation(272, "ai.ask.route", "Ask a question against a knowledge collection over HTTP", [
|
|
160
|
+
route("POST", "/v1/{collection}/_ask", ["collection"], [], true),
|
|
161
|
+
]),
|
|
162
|
+
operation(148, "ai.audit", "Read the agent requests of the app's audit trail (action agent.request in audit.list), newest first: each has its actor, the operation it called and its outcome. Options: since (Unix seconds; the last hour when neither since nor context_id is given), limit (default 50), context_id (an agent session id) and tool_name (needs since or context_id)", [
|
|
163
|
+
route("GET", "/v1/_ai/audit", [], ["limit","since","context_id","tool_name"], false),
|
|
164
|
+
]),
|
|
165
|
+
operation(286, "ai.config.delete", "Delete the app's AI provider configuration", [
|
|
166
|
+
route("DELETE", "/v1/_ai/config", [], [], false),
|
|
167
|
+
]),
|
|
168
|
+
operation(283, "ai.config.get", "Read the app's AI provider configuration", [
|
|
169
|
+
route("GET", "/v1/_ai/config", [], [], false),
|
|
170
|
+
]),
|
|
171
|
+
operation(284, "ai.config.set", "Replace the app's AI provider configuration; see the schema", [
|
|
172
|
+
route("POST", "/v1/_ai/config", [], [], true),
|
|
173
|
+
]),
|
|
174
|
+
operation(285, "ai.config.update", "Change part of the app's AI provider configuration; see the schema", [
|
|
175
|
+
route("PATCH", "/v1/_ai/config", [], [], true),
|
|
176
|
+
]),
|
|
177
|
+
operation(273, "ai.cross_search", "Search multiple vector fields in one collection", [
|
|
178
|
+
route("POST", "/v1/{collection}/_cross_search", ["collection"], [], true),
|
|
179
|
+
]),
|
|
180
|
+
operation(268, "ai.expand.route", "Expand one knowledge chunk of a collection with its surrounding chunks; see the schema", [
|
|
181
|
+
route("POST", "/v1/{collection}/_expand", ["collection"], [], true),
|
|
182
|
+
]),
|
|
183
|
+
operation(271, "ai.feedback.analytics", "Read aggregated search feedback", [
|
|
184
|
+
route("GET", "/v1/{collection}/_feedback/analytics", ["collection"], [], false),
|
|
185
|
+
]),
|
|
186
|
+
operation(269, "ai.feedback.list", "List recent search feedback", [
|
|
187
|
+
route("GET", "/v1/{collection}/_feedback", ["collection"], [], false),
|
|
188
|
+
]),
|
|
189
|
+
operation(270, "ai.feedback.write", "Record search relevance feedback over HTTP", [
|
|
190
|
+
route("POST", "/v1/{collection}/_feedback", ["collection"], [], true),
|
|
191
|
+
]),
|
|
192
|
+
operation(315, "ai.models", "List the distinct provider and model pairs the app's collections' RAG configurations use", [
|
|
193
|
+
route("GET", "/v1/_models", [], [], false),
|
|
194
|
+
]),
|
|
195
|
+
operation(346, "ai.tool.invoke", "Invoke a discovered administrative tool by operation ID", [
|
|
196
|
+
route("POST", "/v1/_tools/invoke", [], [], true),
|
|
197
|
+
]),
|
|
198
|
+
operation(149, "ai.tools", "Describe administration tools available to the caller", [
|
|
199
|
+
route("GET", "/v1/_tools", [], [], false),
|
|
200
|
+
]),
|
|
201
|
+
operation(779, "app.archive", "Download the app as one .substrio.zip, its backup and its portable export: every collection as NDJSON (data/<collection>.ndjson), its users (users.ndjson), versions, config and site files, and manifest.json with each entry's SHA-256. Optional body passphrase seals secret values, password hashes, TOTP enrolments and passkeys into sealed.bin; without it they are left out. Optional backup_id archives that backup instead. app.restore or app.import reads it back on any server version", [
|
|
202
|
+
route("POST", "/v1/_archive/download", [], [], true),
|
|
203
|
+
]),
|
|
204
|
+
operation(749, "app.deploy", "Ship files in one call: commit the units and removals (as version.commit takes them), check the version's functions (unless check is false), make it live where to names (live, or a sandbox), test it there with app.test (unless test is false), and, with promote and a sandbox in to, make it live on the app once the test is green. Stops at the first step that fails: a refused commit, check or live answers that step's own error with stage; a failed test answers ok false, stage test and the test's failures with their fixes, and promotes nothing. Answers ok, stage, deployed {commit, live}, test and promote, each the answer of that step's own tool. Over HTTP it runs the commit, the check and live; test and promote run in the MCP tool", [
|
|
205
|
+
route("POST", "/v1/_app/deploy", [], [], true),
|
|
206
|
+
]),
|
|
207
|
+
operation(751, "app.diagnose", "What is wrong with the app (or a sandbox) and how to fix it, in one call: which version each environment runs against main's head, function.check over the version that runs where the call looks (its diagnostics), its newest function errors, and app.test of its website and GET routes, each finding with its fix. Answers ok, sandbox, live {main, environments}, check, errors {recent, fix}, test and fixes, every fix in order. Read only; the MCP server runs it", [
|
|
208
|
+
route("POST", "/v1/_app/diagnose", [], [], true),
|
|
209
|
+
]),
|
|
210
|
+
operation(413, "app.export", "Stream the app as one bundle (versions, blobs, live stack, views, AI config, database, secrets), sealed whole to seal_to, for that server's POST /v1/_import", [
|
|
211
|
+
route("GET", "/v1/_export", [], ["seal_to"], false),
|
|
212
|
+
]),
|
|
213
|
+
operation(770, "app.get", "Read this app's own record: slug, hosts, domains, suspended, readonly, live_by_promote, account_id, created_at and its limits. The app is the one the request names (X-Substrio-Tenant or its host)", [
|
|
214
|
+
route("GET", "/v1/_app", [], [], false),
|
|
215
|
+
]),
|
|
216
|
+
operation(417, "app.import", "Make an app from a bundle GET /v1/_export streamed (the request body): its data, versions, live version, secrets sealed to this server, views and sandboxes", [
|
|
217
|
+
route("POST", "/v1/_import", [], ["slug","replace"], true),
|
|
218
|
+
]),
|
|
219
|
+
operation(622, "app.mesh", "Read the mesh as this app sees it: enabled, node_id (hex), origin_seq (this node's own last origin sequence), max_applied_hlc (the highest HLC applied), entries (applied, streamed, deduped, pending), peer_count and peers (peer_id, node_id, address, connected, last_sent_seq, latency_us, state, acked_seq and behind, from gossip, null until the peer has gossiped since this process started). An app with no sync answers {\"enabled\": false}", [
|
|
220
|
+
route("GET", "/v1/_mesh/app", [], [], false),
|
|
221
|
+
]),
|
|
222
|
+
operation(620, "app.overview", "Read everything a dashboard's first paint needs for this app in one call: app (slug, tenant_id, created_at, hosts, domains, suspended, live_by_promote, is_sandbox), usage (the same body as tenant.usage.self), storage (database_bytes on disk and backups: count, bytes, latest_at), versions (live, rollback_to, count, sandboxes), functions (count, names), collections (count), cron (count), queues (count), alerts (log alerts fired in their last window), audit (the last 5 entries, newest first) and mesh (this app's mesh position, in short)", [
|
|
223
|
+
route("GET", "/v1/_overview", [], [], false),
|
|
224
|
+
]),
|
|
225
|
+
operation(780, "app.restore", "Restore a .substrio.zip (the request body, as app.archive made it) into this app through the normal write paths: users, data, versions, site files, config, and with the X-Substrio-Passphrase header the sealed secrets and sign-ins. Every entry is checked against manifest.json before anything is written. An app that holds data is refused unless replace=true. Answers the counts restored and what was left out", [
|
|
226
|
+
route("POST", "/v1/_archive", [], ["replace"], true),
|
|
227
|
+
]),
|
|
228
|
+
operation(662, "app.runtime_level.set", "Set one app's runtime compatibility level: which behaviour of the function runtime it gets. Never above the level this release carries (a higher level refuses the call), and the app's isolates pick the new level up as they are made", [
|
|
229
|
+
route("PATCH", "/v1/_tenants/{slug}/runtime_level", ["slug"], [], true),
|
|
230
|
+
]),
|
|
231
|
+
operation(750, "app.scaffold", "Start an app from a template compiled into the release: commits the app's first version on main, one snapshot of the template's units, each of which passes app.test. site is a page with its stylesheet and script; site-api adds a function route (GET /api/hello) the page calls; site-data-auth is a notes page anyone reads and a signed-in person adds to (GET /api/notes, POST /api/notes auth, GET /api/me auth). Refused with 409 when main already holds a version: change an app that has one with version.commit or app.deploy. Answers version.commit's answer and template; make it live with live.set (with sandbox to try it first)", [
|
|
232
|
+
route("POST", "/v1/_app/scaffold", [], [], true),
|
|
233
|
+
]),
|
|
234
|
+
operation(412, "app.seal_key.get", "Read the X25519 public key bundles this server imports are sealed to (seal_to of an export)", [
|
|
235
|
+
route("GET", "/v1/_seal-key", [], [], false),
|
|
236
|
+
]),
|
|
237
|
+
operation(748, "app.test", "Test the app's website (or a sandbox's) as a browser loads it, in one call: each page of paths (default /) and every same-site page it links, every stylesheet, script, image, icon and CSS url() or @import they name, and every GET function route with no parameter, each fetched in process as route.call does, as a visitor with no credentials. Each is checked for its status, its content type (a stylesheet must be text/css, a script JavaScript, an image image/*, never the site's page in their place) and its size. Answers ok, the table of every resource (path, kind, from, status, type, bytes, ok) and each failure with its problem and its fix. References to another origin are counted, never fetched", [
|
|
238
|
+
route("POST", "/v1/_app/test", [], [], true),
|
|
239
|
+
]),
|
|
240
|
+
operation(669, "apps.calls.get", "Read the app's word on calls between apps: the functions it offers other apps (each to the apps named, or @account: every app of its own account) and the functions of other apps it calls as substrio.apps.<app>.<entry>(...)", [
|
|
241
|
+
route("GET", "/v1/_app_calls", [], [], false),
|
|
242
|
+
]),
|
|
243
|
+
operation(670, "apps.calls.set", "Replace the app's word on calls between apps with the body (an empty body object removes it). A call needs both sides: the callee offers the entry to the caller, the caller names it under use. A bad document is refused whole and the word in force stays; substrio deploy sends it from substrio.toml [apps.offer] and [apps.use]. A read offered under query (substrio.apps.<app>.query.<name>(...args)) is run by the engine on this app's store with no function of it run, under the same two-sided grant (the caller names it under use_query; [apps.query.<name>] and [apps.use_query]); every where field but _id must be declared indexed (index.create kind collection), else the document is refused", [
|
|
244
|
+
route("PUT", "/v1/_app_calls", [], [], true),
|
|
245
|
+
]),
|
|
246
|
+
operation(565, "audit.export", "Stream the app's audit trail, oldest first, as one JSON object per line (application/x-ndjson): every record of audit.list plus its chained (whether it carries a digest) and digest (hex, when chained) fields, and a final {chain_head_seq, chain_head_digest} line on the last page. Filters: since and until (Unix seconds); limit (default 1000) and cursor (the X-Substrio-Next-Cursor response header) page it; a page keeps the chain order, so a customer can verify a kept export later without substrio", [
|
|
247
|
+
route("GET", "/v1/_audit/export", [], ["since","limit","cursor","until"], false),
|
|
248
|
+
]),
|
|
249
|
+
operation(561, "audit.get", "Read one audit record of the app's trail by its seq (the number audit.list shows for it)", [
|
|
250
|
+
route("GET", "/v1/_audit/{seq}", ["seq"], [], false),
|
|
251
|
+
]),
|
|
252
|
+
operation(560, "audit.list", "Read the app's audit trail, newest first: every deploy, promote, rollback, secret, key, token, setting, rule, collection, index and domain change, and every agent request, each with its actor, target, before and after fingerprints and outcome (applied, refused, failed, interrupted, or pending while it runs). Filters: action, actor_kind with actor, target_kind with target, outcome, since and until (Unix seconds), order; limit and cursor page it. Answers records, count and next_cursor. A record is never removed or changed; a change has its record before it runs", [
|
|
253
|
+
route("GET", "/v1/_audit", [], ["order","since","limit","cursor","until","action","actor_kind","actor","target_kind","target","outcome"], false),
|
|
254
|
+
]),
|
|
255
|
+
operation(564, "audit.verify", "Walk the app's audit trail over [from, to] (default: the whole trail) and answer whether every record's digest chains from the one before it to the chain head: intact, or the first seq where it breaks and how (digest_mismatch, gap, or head_mismatch). Reads each record once, in order; records_read is the rows it touched", [
|
|
256
|
+
route("GET", "/v1/_audit/verify", [], ["from","to"], false),
|
|
257
|
+
]),
|
|
258
|
+
operation(21, "auth.agent_token.create", "Create an agent token that acts as the signed-in user; the secret is returned once", [
|
|
259
|
+
route("POST", "/v1/_auth/agent_tokens", [], [], true),
|
|
260
|
+
]),
|
|
261
|
+
operation(22, "auth.agent_token.list", "List the signed-in user's agent tokens; a token's secret is shown only when it is created (auth.agent_token.create)", [
|
|
262
|
+
route("GET", "/v1/_auth/agent_tokens", [], [], false),
|
|
263
|
+
]),
|
|
264
|
+
operation(23, "auth.agent_token.revoke", "Revoke one of the signed-in user's agent tokens", [
|
|
265
|
+
route("DELETE", "/v1/_auth/agent_tokens/{id}", ["id"], [], false),
|
|
266
|
+
]),
|
|
267
|
+
operation(407, "auth.client.create", "Let one of the account's apps sign people in through the identity app, sending them back only to these redirect URIs (exact match)", [
|
|
268
|
+
route("POST", "/v1/_accounts/{id}/clients", ["id"], [], true),
|
|
269
|
+
]),
|
|
270
|
+
operation(409, "auth.client.delete", "Stop an app signing people in through the identity app", [
|
|
271
|
+
route("DELETE", "/v1/_accounts/{id}/clients/{slug}", ["id","slug"], [], false),
|
|
272
|
+
]),
|
|
273
|
+
operation(406, "auth.client.list", "List the account's apps that sign people in through the identity app, with their redirect URIs", [
|
|
274
|
+
route("GET", "/v1/_accounts/{id}/clients", ["id"], [], false),
|
|
275
|
+
]),
|
|
276
|
+
operation(408, "auth.client.redirect_uris.set", "Replace the redirect URIs an app's sign-ins may return to", [
|
|
277
|
+
route("PUT", "/v1/_accounts/{id}/clients/{slug}", ["id","slug"], [], true),
|
|
278
|
+
]),
|
|
279
|
+
operation(790, "auth.devices", "Count the signed-in person's remembered devices: each signs in with the authenticator code and no email until it ends (30 days, or every session of the person ending)", [
|
|
280
|
+
route("GET", "/v1/_auth/devices", [], [], false),
|
|
281
|
+
]),
|
|
282
|
+
operation(791, "auth.devices.forget", "Forget every remembered device of the signed-in person: each signs in by the email code again. No session ends", [
|
|
283
|
+
route("POST", "/v1/_auth/devices/forget", [], [], false),
|
|
284
|
+
]),
|
|
285
|
+
operation(610, "auth.email.code_request", "Mail a short code and a link that prove an address; nobody need be signed in. One pair an address: asked again while it lives, the same code and link are mailed again (every mail sent works) and its life starts over; a spent, expired or locked pair gives way to a new one. Paced by the app's request interval. Answers {accepted, expires_at} whether or not anyone holds the address. On a device remembered for the person the address names (the substrio_device cookie auth.mfa set), their 2FA on, no mail is sent: {accepted, expires_at, mfa_required, challenge}, finished by auth.mfa with the authenticator code", [
|
|
286
|
+
route("POST", "/v1/_auth/email/code_request", [], [], true),
|
|
287
|
+
]),
|
|
288
|
+
operation(611, "auth.email.code_verify", "Prove an address with its mailed code (email and code) or its link (token): the person holding it, made when nobody does, is verified. Answers {verified, email, user_id, created, password_grant?}; a code or link that proves nothing is 400 with its reason (MISSING, USED, EXPIRED, TOO_MANY_ATTEMPTS, WRONG and attempts_remaining)", [
|
|
289
|
+
route("POST", "/v1/_auth/email/code_verify", [], [], true),
|
|
290
|
+
]),
|
|
291
|
+
operation(386, "auth.email.verify", "Verify an email with the token from its link", [
|
|
292
|
+
route("POST", "/v1/_auth/email/verify", [], [], true),
|
|
293
|
+
]),
|
|
294
|
+
operation(385, "auth.email.verify_request", "Mail the signed-in user a link that verifies their email", [
|
|
295
|
+
route("POST", "/v1/_auth/email/verify_request", [], [], false),
|
|
296
|
+
]),
|
|
297
|
+
operation(252, "auth.github", "Authenticate with GitHub OAuth", [
|
|
298
|
+
route("POST", "/v1/_auth/github", [], [], true),
|
|
299
|
+
]),
|
|
300
|
+
operation(251, "auth.google", "Authenticate with Google OAuth", [
|
|
301
|
+
route("POST", "/v1/_auth/google", [], [], true),
|
|
302
|
+
]),
|
|
303
|
+
operation(744, "auth.link.redeem", "For the console: spend a one-time console link (token, from the url's #link fragment) and sign its person in at the identity app, marked: an irreversible call waits for an approval and it decides none (CONSOLE_LINK_STEP_UP) until the person signs in again by the email code or 2FA. Answers {user_id, account_id, token, keyring} as auth.email.code_verify signs in, or the 2FA step due under mfa; a link that signs nobody in is refused CONSOLE_LINK_UNKNOWN (made up, or replaced by a newer link), CONSOLE_LINK_USED or CONSOLE_LINK_EXPIRED", [
|
|
304
|
+
route("POST", "/v1/_auth/link/redeem", [], [], true),
|
|
305
|
+
]),
|
|
306
|
+
operation(17, "auth.login", "Authenticate a user", [
|
|
307
|
+
route("POST", "/v1/_auth/login", [], [], true),
|
|
308
|
+
route("POST", "/v1/auth/login", [], [], true),
|
|
309
|
+
]),
|
|
310
|
+
operation(447, "auth.login.set", "Join the shared login ([login]): make the app a client of the identity app with a callback on each of its hosts", [
|
|
311
|
+
route("PUT", "/v1/_auth/login", [], [], true),
|
|
312
|
+
]),
|
|
313
|
+
operation(380, "auth.logout", "End this session; its token stops working at once, and this browser's substrio_session cookie ends and is cleared (a remembered device stays)", [
|
|
314
|
+
route("POST", "/v1/_auth/logout", [], [], false),
|
|
315
|
+
]),
|
|
316
|
+
operation(381, "auth.logout_all", "End every session of the signed-in user, this one included", [
|
|
317
|
+
route("POST", "/v1/_auth/logout_all", [], [], false),
|
|
318
|
+
]),
|
|
319
|
+
operation(379, "auth.me", "Read the signed-in user", [
|
|
320
|
+
route("GET", "/v1/_auth/me", [], [], false),
|
|
321
|
+
]),
|
|
322
|
+
operation(607, "auth.me.delete", "Erase the signed-in user and their auth records; dry_run checks only", [
|
|
323
|
+
route("DELETE", "/v1/_auth/me", [], ["dry_run"], false),
|
|
324
|
+
]),
|
|
325
|
+
operation(792, "auth.me.update", "Edit the signed-in person's own profile: name always, and each member of fields only when the app's _users schema marks that field \"self\": true (a member not marked refuses the whole edit with 403 SELF_FIELD_REFUSED naming it; a value the schema refuses is 400 SCHEMA_REFUSED; a key is refused 403, a key is not a user). Email and password change through their own verified flows. Answers the profile as auth.me does", [
|
|
326
|
+
route("PATCH", "/v1/_auth/me", [], [], true),
|
|
327
|
+
]),
|
|
328
|
+
operation(418, "auth.mfa", "Finish a sign-in that answered mfa_required: trade its challenge and a code, or a recovery code, for the session. After the email code, remember true also remembers the device (the substrio_device cookie, 30 days)", [
|
|
329
|
+
route("POST", "/v1/_auth/mfa", [], [], true),
|
|
330
|
+
]),
|
|
331
|
+
operation(421, "auth.mfa.disable", "Turn MFA off with a code or a recovery code; every older session ends at its refresh", [
|
|
332
|
+
route("POST", "/v1/_auth/mfa/disable", [], [], true),
|
|
333
|
+
]),
|
|
334
|
+
operation(422, "auth.mfa.recovery.regenerate", "Replace the recovery codes, with a code from the authenticator app; the old ones stop working", [
|
|
335
|
+
route("POST", "/v1/_auth/mfa/recovery_codes", [], [], true),
|
|
336
|
+
]),
|
|
337
|
+
operation(420, "auth.mfa.totp.confirm", "Confirm the authenticator app with a code: MFA is on, the recovery codes are shown once, and every older session ends at its refresh", [
|
|
338
|
+
route("POST", "/v1/_auth/mfa/totp/confirm", [], [], true),
|
|
339
|
+
]),
|
|
340
|
+
operation(419, "auth.mfa.totp.start", "Start enrolling an authenticator app: returns its secret and otpauth URI; MFA is on once a code confirms it", [
|
|
341
|
+
route("POST", "/v1/_auth/mfa/totp", [], [], true),
|
|
342
|
+
]),
|
|
343
|
+
operation(250, "auth.oauth", "Authenticate with an OAuth provider", [
|
|
344
|
+
route("POST", "/v1/_auth/oauth", [], [], true),
|
|
345
|
+
]),
|
|
346
|
+
operation(410, "auth.oidc.key.rotate", "Sign id_tokens with a new key; the key it replaces stays in the JWKS until the next rotation", [
|
|
347
|
+
route("POST", "/v1/_auth/oidc/keys/rotate", [], [], false),
|
|
348
|
+
]),
|
|
349
|
+
operation(430, "auth.passkey.delete", "Remove one of the signed-in person's passkeys; it signs in no more", [
|
|
350
|
+
route("DELETE", "/v1/_auth/passkeys/{id}", ["id"], [], false),
|
|
351
|
+
]),
|
|
352
|
+
operation(428, "auth.passkey.list", "List the signed-in person's passkeys", [
|
|
353
|
+
route("GET", "/v1/_auth/passkeys", [], [], false),
|
|
354
|
+
]),
|
|
355
|
+
operation(425, "auth.passkey.register", "Finish registering a passkey: verifies the attestation (none, or packed self-attestation) and stores the credential", [
|
|
356
|
+
route("POST", "/v1/_auth/passkeys/register", [], [], true),
|
|
357
|
+
]),
|
|
358
|
+
operation(424, "auth.passkey.register.options", "Start registering a passkey: returns the WebAuthn creation options (challenge, rp, user handle, ES256 and RS256) for navigator.credentials.create", [
|
|
359
|
+
route("POST", "/v1/_auth/passkeys/register/options", [], [], false),
|
|
360
|
+
]),
|
|
361
|
+
operation(429, "auth.passkey.rename", "Rename one of the signed-in person's passkeys", [
|
|
362
|
+
route("PATCH", "/v1/_auth/passkeys/{id}", ["id"], [], true),
|
|
363
|
+
]),
|
|
364
|
+
operation(427, "auth.passkey.sign_in", "Sign in with a passkey and no password: verifies the assertion and returns a session; a passkey that verified the person needs no 2FA", [
|
|
365
|
+
route("POST", "/v1/_auth/passkeys/sign-in", [], [], true),
|
|
366
|
+
]),
|
|
367
|
+
operation(426, "auth.passkey.sign_in.options", "Start a passkey sign-in: returns the WebAuthn request options for navigator.credentials.get, for a discoverable passkey or a person's by email", [
|
|
368
|
+
route("POST", "/v1/_auth/passkeys/sign-in/options", [], [], true),
|
|
369
|
+
]),
|
|
370
|
+
operation(382, "auth.password.change", "Change the signed-in user's password; every other session ends", [
|
|
371
|
+
route("POST", "/v1/_auth/password", [], [], true),
|
|
372
|
+
]),
|
|
373
|
+
operation(384, "auth.password.reset", "Set a new password with a reset token; every session ends", [
|
|
374
|
+
route("POST", "/v1/_auth/password/reset", [], [], true),
|
|
375
|
+
]),
|
|
376
|
+
operation(383, "auth.password.reset_request", "Mail a password reset link, if the email belongs to a user", [
|
|
377
|
+
route("POST", "/v1/_auth/password/reset_request", [], [], true),
|
|
378
|
+
]),
|
|
379
|
+
operation(253, "auth.provider.delete", "Remove one identity provider of the app by name", [
|
|
380
|
+
route("DELETE", "/v1/_auth/providers", [], [], true),
|
|
381
|
+
route("DELETE", "/v1/_auth/providers/{provider}", ["provider"], [], false),
|
|
382
|
+
]),
|
|
383
|
+
operation(312, "auth.provider.get", "Read one identity provider of the app by name", [
|
|
384
|
+
route("GET", "/v1/_auth/providers/{provider}", ["provider"], [], false),
|
|
385
|
+
]),
|
|
386
|
+
operation(19, "auth.provider.list", "List the app's identity providers (social sign-in)", [
|
|
387
|
+
route("GET", "/v1/_auth/providers", [], [], false),
|
|
388
|
+
]),
|
|
389
|
+
operation(20, "auth.provider.update", "Configure one identity provider of the app for social sign-in; see the schema for its body", [
|
|
390
|
+
route("POST", "/v1/_auth/providers", [], [], true),
|
|
391
|
+
]),
|
|
392
|
+
operation(18, "auth.refresh", "Refresh a session: the Bearer token for a new one (the old one stops working). stay true also sets the substrio_session cookie (30 days, renewed by each refresh); with no Bearer, that cookie alone signs its person in again as their sign-in did", [
|
|
393
|
+
route("POST", "/v1/_auth/refresh", [], [], true),
|
|
394
|
+
route("POST", "/v1/auth/refresh", [], [], true),
|
|
395
|
+
]),
|
|
396
|
+
operation(16, "auth.register", "Create a user session", [
|
|
397
|
+
route("POST", "/v1/_auth/register", [], [], true),
|
|
398
|
+
route("POST", "/v1/auth/signup", [], [], true),
|
|
399
|
+
]),
|
|
400
|
+
operation(387, "auth.settings.get", "Read the app's auth settings: its email function and link URLs", [
|
|
401
|
+
route("GET", "/v1/_auth/settings", [], [], false),
|
|
402
|
+
]),
|
|
403
|
+
operation(388, "auth.settings.set", "Set the app's auth settings: its email function, link URLs and token lifetimes", [
|
|
404
|
+
route("PUT", "/v1/_auth/settings", [], [], true),
|
|
405
|
+
]),
|
|
406
|
+
operation(416, "auth.shared_provider.delete", "Remove the identity app's shared registration at a social provider", [
|
|
407
|
+
route("DELETE", "/v1/_auth/shared-providers/{provider}", ["provider"], [], false),
|
|
408
|
+
]),
|
|
409
|
+
operation(414, "auth.shared_provider.list", "List the identity app's shared social sign-in registrations (no secrets)", [
|
|
410
|
+
route("GET", "/v1/_auth/shared-providers", [], [], false),
|
|
411
|
+
]),
|
|
412
|
+
operation(415, "auth.shared_provider.set", "Set the identity app's one registration at a social provider; every app without its own signs people in with it, at the identity app", [
|
|
413
|
+
route("PUT", "/v1/_auth/shared-providers/{provider}", ["provider"], [], true),
|
|
414
|
+
]),
|
|
415
|
+
operation(411, "auth.substrio", "Sign in with the identity app: trade its code for a session of this app", [
|
|
416
|
+
route("POST", "/v1/_auth/substrio", [], [], true),
|
|
417
|
+
]),
|
|
418
|
+
operation(449, "auth.whoami", "Which account and app a key belongs to, and its scopes", [
|
|
419
|
+
route("GET", "/v1/_whoami", [], [], false),
|
|
420
|
+
route("GET", "/_whoami", [], [], false),
|
|
421
|
+
]),
|
|
422
|
+
operation(614, "backup.clone_into", "Replace another tenant's database (body target_slug) with a backup of this app (the route's {id} from backup.list), then catch it up with this app's history from the backup's point to now, so it ends up current, not frozen at the backup. Everything target_slug held is lost; it must be a different tenant. Answers {status:'cloned', backup_id, target_slug, history_position, history_through, history_applied}", [
|
|
423
|
+
route("POST", "/v1/_backups/{id}/clone_into", ["id"], [], true),
|
|
424
|
+
]),
|
|
425
|
+
operation(193, "backup.create", "Start a backup of the app's database as a background job: optional body description. Answers 202 {job_id, backup_id, status:'running'} at once, the backup already listed by backup.list as running; backup.get with the job id shows the progress, and with the backup id the backup that backup.restore takes once it completes", [
|
|
426
|
+
route("POST", "/v1/_backups", [], [], true),
|
|
427
|
+
]),
|
|
428
|
+
operation(294, "backup.delete", "Delete one backup by its id (the route's {id}, as backup.list shows it)", [
|
|
429
|
+
route("DELETE", "/v1/_backups/{id}", ["id"], [], false),
|
|
430
|
+
]),
|
|
431
|
+
operation(293, "backup.get", "Read one backup or backup job by id: the job_id backup.create answered (its status and progress) or a backup id from backup.list (its details); 404 when neither exists", [
|
|
432
|
+
route("GET", "/v1/_backups/{id}", ["id"], [], false),
|
|
433
|
+
]),
|
|
434
|
+
operation(192, "backup.list", "List the app's backups, oldest first, a page at a time (limit, cursor; next_cursor): their ids are what backup.restore, backup.get and backup.delete take", [
|
|
435
|
+
route("GET", "/v1/_backups", [], ["limit","cursor"], false),
|
|
436
|
+
]),
|
|
437
|
+
operation(194, "backup.restore", "Replace the app's database with a backup: the route's {id} from backup.list. Everything written since the backup is lost, so take a backup first (backup.create). Answers {status:'restored', backup_id}. To go back to a promote's state together with its version use version.restore", [
|
|
438
|
+
route("POST", "/v1/_backups/{id}/restore", ["id"], [], false),
|
|
439
|
+
]),
|
|
440
|
+
operation(255, "batch.execute", "Execute an atomic batch of typed operations", [
|
|
441
|
+
route("POST", "/v1/_batch", [], [], true),
|
|
442
|
+
]),
|
|
443
|
+
operation(667, "billing.accounts", "List every account the payment provider has told the platform of, in account id order, a page at a time (limit, cursor): its plan, the provider's customer and subscription, the payment state of its newest invoice (paid or past_due), and paid: the cleared sum of its paid invoices in its plan's currency, the identity app ledger's maintained balance (kind billing, party the account id). Its invoices one by one are ledger.entries by party on the identity app", [
|
|
444
|
+
route("GET", "/v1/_billing/accounts", [], ["limit","cursor"], false),
|
|
445
|
+
]),
|
|
446
|
+
operation(711, "billing.cart.create", "Make a cart of plans (a featured app's a la carte, or a Substrio plan) and funds for the account's credit. The answer prices it from the plans files, offers the Substrio plan that covers its apps for less, and gives checkout_url: the page the person pays on. Nothing is charged", [
|
|
447
|
+
route("POST", "/v1/_accounts/{id}/billing/carts", ["id"], [], true),
|
|
448
|
+
]),
|
|
449
|
+
operation(712, "billing.cart.get", "Read one of the account's carts, priced as the plans are now, with its state (open, paying, paid) and checkout_url", [
|
|
450
|
+
route("GET", "/v1/_accounts/{id}/billing/carts/{cart}", ["id","cart"], [], false),
|
|
451
|
+
]),
|
|
452
|
+
operation(713, "billing.cart.update", "Replace what an open cart buys (its items and funds), for example to take the Substrio plan it offers; a cart whose checkout started no longer changes", [
|
|
453
|
+
route("PUT", "/v1/_accounts/{id}/billing/carts/{cart}", ["id","cart"], [], true),
|
|
454
|
+
]),
|
|
455
|
+
operation(710, "billing.catalog", "What the account can buy: the Substrio plans (each with the featured apps it bundles), every featured app's own plans a la carte with prices, the plan the account is on at each, and whether checkout is ready", [
|
|
456
|
+
route("GET", "/v1/_accounts/{id}/billing/catalog", ["id"], [], false),
|
|
457
|
+
]),
|
|
458
|
+
operation(715, "billing.checkout.confirm", "After the person paid on the page: reads the provider's word for the cart's payment and applies the cart once (its plans put on, its funds added to credit); the webhook applies it too, whichever comes first. Answers the cart, state paid", [
|
|
459
|
+
route("POST", "/v1/_accounts/{id}/billing/carts/{cart}/confirm", ["id","cart"], [], false),
|
|
460
|
+
]),
|
|
461
|
+
operation(714, "billing.checkout.start", "Start paying a cart (the person's own session only, never an agent's or a key): the provider's payment is made for its plans and funds and the answer gives the client_secret and publishable_key the checkout page's Payment Element pays with. A change the saved card pays at once is applied and answers paid true", [
|
|
462
|
+
route("POST", "/v1/_accounts/{id}/billing/carts/{cart}/checkout", ["id","cart"], [], false),
|
|
463
|
+
]),
|
|
464
|
+
operation(716, "billing.credit.get", "Read the account's credit: the funds it added, in the minor unit, and how many payments made it", [
|
|
465
|
+
route("GET", "/v1/_accounts/{id}/billing/credit", ["id"], [], false),
|
|
466
|
+
]),
|
|
467
|
+
operation(752, "billing.person_cart.create", "Make a cart of plans that are the person's own, paid by the account: they hold them at every app wherever they sign in, apart from the account's (subscriptions.mine lists them). The plans, the offer and checkout_url as billing.cart.create gives them; the subject is the person whose own session starts the checkout. No space_blocks: the add-on is the account's", [
|
|
468
|
+
route("POST", "/v1/_accounts/{id}/billing/person-carts", ["id"], [], true),
|
|
469
|
+
]),
|
|
470
|
+
operation(718, "billing.revenue", "What every account has paid the platform (paid: the identity ledger's maintained balance of the platform party, in USD, with how many payments) and how many accounts' newest invoice is unpaid (past_due). Platform operator only", [
|
|
471
|
+
route("GET", "/v1/_billing/revenue", [], [], false),
|
|
472
|
+
]),
|
|
473
|
+
operation(717, "billing.subscription.cancel", "End the account's subscription, at the end of the period paid for (the default) or now. When it ends, the Substrio plan and every featured app it paid for go back to their free plans, still enabled", [
|
|
474
|
+
route("POST", "/v1/_accounts/{id}/billing/subscription/cancel", ["id"], [], true),
|
|
475
|
+
]),
|
|
476
|
+
operation(624, "branch.create", "Make a branch in the app's history: name it and say where it starts (from, a branch name or a version id; default the main branch's head). A branch is a name that moves with each commit on it. 409 when the name is taken; the name of a deleted branch is free again and its log is kept. Answers {name, version, protected}", [
|
|
477
|
+
route("POST", "/v1/_branches", [], [], true),
|
|
478
|
+
]),
|
|
479
|
+
operation(626, "branch.delete", "Free a branch's name (the route's {name}). Its versions are kept (another name or a merge may still reach them) and its log survives, so ref.restore can put the name back. The main branch is never deleted. Answers {name, version}", [
|
|
480
|
+
route("DELETE", "/v1/_branches/{name}", ["name"], [], false),
|
|
481
|
+
]),
|
|
482
|
+
operation(625, "branch.list", "List the app's branches by name, each with the version it points at, when it last moved, whether it is protected and the checks a move onto it needs green; never refuses", [
|
|
483
|
+
route("GET", "/v1/_branches", [], [], false),
|
|
484
|
+
]),
|
|
485
|
+
operation(627, "branch.protect", "Protect a branch (the route's {name}), or unprotect it with protected:false. While it is protected every commit or merge onto it must say its required_checks are green. An empty required_checks protects it with no named check, which still needs a green verdict. Answers {name, protected, required_checks}", [
|
|
486
|
+
route("POST", "/v1/_branches/{name}/protect", ["name"], [], true),
|
|
487
|
+
]),
|
|
488
|
+
operation(663, "cluster.ready", "Whether this node has caught up with the mesh: 200 when every app's replication has nothing outstanding (its links up, no catch-up in flight, and every position its peers have gossiped already held here) and 503 while one has, naming the app it waits on and what it waits for. A rolling upgrade asks this between nodes, with curl alone, before it installs on the next one", [
|
|
489
|
+
route("GET", "/v1/_cluster/ready", [], [], false),
|
|
490
|
+
]),
|
|
491
|
+
operation(660, "cluster.version", "Read the versions in use: the wire range this release speaks, the version chosen for the mesh (the lowest max over the members), the version the mesh has FINALIZED, the storage format, the runtime level this release carries, and every member with its range and whether its link is live", [
|
|
492
|
+
route("GET", "/v1/_cluster/version", [], [], false),
|
|
493
|
+
]),
|
|
494
|
+
operation(661, "cluster.version.finalize", "Finalize the mesh at wire version {version}: allowed only when this node and every member already speak it (a member whose link is down or whose max is lower refuses the call and is named). It is written durably before the call answers and never goes back: from then on a node that does not speak it is refused at the handshake, so finalize only once every node runs the new release", [
|
|
495
|
+
route("POST", "/v1/_cluster/version/finalize", [], [], true),
|
|
496
|
+
]),
|
|
497
|
+
operation(49, "collection.create", "Create an object collection: body name (1 to 64 characters, not starting with an underscore; 409 when it exists), optional acl_flags (integer bit set of the collection's access rules, 0 = none enforced) and identity (field names whose values make a record's key content-addressed). Answers 201 {name, created, acl_flags, identity?}. A schema is separate: schema.update", [
|
|
498
|
+
route("POST", "/v1/_meta/collections", [], [], true),
|
|
499
|
+
]),
|
|
500
|
+
operation(50, "collection.delete", "Delete one collection by name together with its records and its index, vector, text and sparse trees; 404 when it does not exist and 400 for a name starting with an underscore. There is no undo: take a backup first when unsure", [
|
|
501
|
+
route("DELETE", "/v1/_meta/collections/{name}", ["name"], [], false),
|
|
502
|
+
]),
|
|
503
|
+
operation(48, "collection.list", "List the app's collections, paged: the limit and cursor options in, next_cursor out. Call it first to learn what data exists; collection.stats counts one collection's records", [
|
|
504
|
+
route("GET", "/v1/_collections", [], ["limit","cursor"], false),
|
|
505
|
+
route("GET", "/v1/_meta/collections", [], ["limit","cursor"], false),
|
|
506
|
+
]),
|
|
507
|
+
operation(51, "collection.stats", "Read one collection's statistics: doc_count, indexes (each named: {kind: field, field, unique} for a declared field, {kind: vector|text|sparse} for a search index) with index_count, their number, and has_rag; 404 when it does not exist", [
|
|
508
|
+
route("GET", "/v1/{collection}/_stats", ["collection"], [], false),
|
|
509
|
+
route("GET", "/v1/{collection}/stats", ["collection"], [], false),
|
|
510
|
+
]),
|
|
511
|
+
operation(785, "dashboard.get", "Read the dashboard an app declares, the file its live version shipped (nav items and their panels, canonical), for the console to draw. The app is one of the account's own or a featured app the account has enabled; any other answers TENANT_NOT_FOUND. An app that declares none answers an empty nav. With version (a number or id), that version's dashboard instead, checked against its own routes as a push's is: what the console will draw once it goes live (an app of the account's own)", [
|
|
512
|
+
route("GET", "/v1/_accounts/{id}/apps/{slug}/dashboard", ["id","slug"], ["version"], false),
|
|
513
|
+
]),
|
|
514
|
+
operation(68, "data.delete", "Delete one record by its _id; 404 when it does not exist, 403 when the key may not delete it", [
|
|
515
|
+
route("DELETE", "/v1/{collection}/{id}", ["collection","id"], [], false),
|
|
516
|
+
]),
|
|
517
|
+
operation(71, "data.export", "Export a collection's records: option format ndjson (the default), json or csv, and limit", [
|
|
518
|
+
route("GET", "/v1/{collection}/_export", ["collection"], ["limit","format"], false),
|
|
519
|
+
]),
|
|
520
|
+
operation(65, "data.get", "Read one record by its _id from the route's {collection}: the stored fields plus _id. 404 when it does not exist, 403 when the key may not read it", [
|
|
521
|
+
route("GET", "/v1/{collection}/{id}", ["collection","id"], [], false),
|
|
522
|
+
]),
|
|
523
|
+
operation(70, "data.import", "Insert many documents into a collection in one call: the body is a JSON array or NDJSON (option format json or ndjson; by default json when it starts with '['). A document that names _id (a base62 string or a positive whole number) is stored under that id, so an export imports back with the same ids and the references between documents still resolve; one without _id gets a generated id. A document is refused alone and the others are written: a document the collection's schema refuses (the check data.insert makes) fails with SCHEMA_REFUSED and lists its violations, an _id that names no document fails with INVALID_ID, an _id a stored document (or an earlier row) already holds fails with KEY_EXISTS unless option on_conflict says otherwise (error, the default: fails that row with KEY_EXISTS, nothing overwritten; skip: the stored document is left as it is, counted in skipped; replace: the row stores over it whole, counted in replaced; skip and replace refuse, alone, a row with no _id). _createdAt, _updatedAt and _version are the system's and ignored: an imported document is stamped anew. Answers {inserted, skipped, replaced, rejected, errors (the index, code and message of each rejected document), total}", [
|
|
524
|
+
route("POST", "/v1/{collection}/_import", ["collection"], ["format","on_conflict"], true, "documents"),
|
|
525
|
+
]),
|
|
526
|
+
operation(66, "data.insert", "Insert one document into a collection: the document is the whole body over HTTP (an object of any fields). The collection assigns _id: a body that names _id is refused with 400 ID_RESERVED and nothing is stored (put(doc) in the function runtime chooses an id); _createdAt, _updatedAt and _version are the system's and ignored. Answers 201 {_id, status:'created'} and runs the collection's insert triggers with that _id. For many documents use data.import", [
|
|
527
|
+
route("POST", "/v1/{collection}", ["collection"], [], true, "document"),
|
|
528
|
+
]),
|
|
529
|
+
operation(64, "data.list", "List a collection's records in key order: options limit (default 100; a larger value is lowered to 1000), offset and cursor (the previous page's next_cursor, a record _id). Answers data (each record with its _id), count, offset, limit and next_cursor. Records the caller may not read are left out. For filters use data.query or query.chain", [
|
|
530
|
+
route("GET", "/v1/{collection}", ["collection"], ["limit","offset","cursor"], false),
|
|
531
|
+
]),
|
|
532
|
+
operation(72, "data.put", "Store one whole document under an id the caller chooses (the route's {id}: a base62 id string or a positive whole number): creates it when no document holds the id, replaces the stored document whole when one does (fields the body leaves out are gone; the stamps are made anew). A body that names _id must name the same id, else 400 ID_MISMATCH; an id that is neither form is 400 INVALID_ID. Needs create on the collection for a new document and write for a replacement; honours the tenant's document and storage quotas (507). Answers {_id, created}; runs the collection's insert trigger for a new document and its update trigger for a replaced one", [
|
|
533
|
+
route("PUT", "/v1/{collection}/{id}", ["collection","id"], [], true, "document"),
|
|
534
|
+
]),
|
|
535
|
+
operation(69, "data.query", "Run a typed query over one collection: a where filter, sort, limit and offset (see the schema); use_index false forces a scan. Answers the matching records. For joins, grouping and aggregates use query.chain; for SQL use query.sql", [
|
|
536
|
+
route("POST", "/v1/{collection}/_query", ["collection"], ["use_index"], true),
|
|
537
|
+
route("GET", "/v1/{collection}/_query", ["collection"], ["use_index","where","select","limit","skip","sort","after","count","explain","timeout"], false),
|
|
538
|
+
]),
|
|
539
|
+
operation(67, "data.update", "Change one record in place with operators: $set (fields to values), $unset (fields to remove), $inc (numeric fields to add to) and $push (array fields to append to); at least one, and fields starting with an underscore are skipped. A $set, $unset or $inc key may reach into arrays: \"items.0.qty\" one element, \"items.$[].qty\" every element, \"items.$[big].qty\" the elements the arrayFilters entry for the identifier big selects (arrayFilters: [{identifier, where, scalar?}]; an identifier with no entry, an entry no key uses or two entries for one identifier answer 400 and nothing is written). 404 when the record does not exist, 403 when the key may not write it", [
|
|
540
|
+
route("PATCH", "/v1/{collection}/{id}", ["collection","id"], [], true),
|
|
541
|
+
]),
|
|
542
|
+
operation(454, "documentation.agent_guide", "Read the guide for AI agents building on substrio (text/plain); a tenant's own /llms.txt function route or site file is served instead", [
|
|
543
|
+
route("GET", "/llms.txt", [], [], false),
|
|
544
|
+
]),
|
|
545
|
+
operation(747, "documentation.brief", "Start here, in at most 3 KB. Answers the app, what this key is, the live version number, the goals an app is built for (website, api, database, auth, events: each with its summary, its skill and the tools it needs), the build loop's tools in order, and how to reach every other tool. Takes no input; substrio_tools {area: <goal>} answers a goal's full definitions, substrio_start every area's tools", [
|
|
546
|
+
route("GET", "/v1/_brief", [], [], false),
|
|
547
|
+
]),
|
|
548
|
+
operation(4, "documentation.capabilities", "Describe typed capabilities", [
|
|
549
|
+
route("GET", "/v1/_capabilities", [], [], false),
|
|
550
|
+
]),
|
|
551
|
+
operation(5, "documentation.openapi", "Read generated OpenAPI", [
|
|
552
|
+
route("GET", "/v1/_openapi", [], [], false),
|
|
553
|
+
]),
|
|
554
|
+
operation(616, "documentation.page.get", "Read one doc page by name (the route's {name}): its Markdown. 404 when no such page. No key needed", [
|
|
555
|
+
route("GET", "/v1/_docs/pages/{name}", ["name"], [], false),
|
|
556
|
+
]),
|
|
557
|
+
operation(615, "documentation.page.list", "List every doc page compiled into this server (name, title, one line); read one with documentation.page.get. No key needed", [
|
|
558
|
+
route("GET", "/v1/_docs/pages", [], [], false),
|
|
559
|
+
]),
|
|
560
|
+
operation(6, "documentation.registry", "Describe the typed operation registry", [
|
|
561
|
+
route("GET", "/v1/_operation_registry", [], [], false),
|
|
562
|
+
]),
|
|
563
|
+
operation(487, "documentation.runtime_types", "Read the TypeScript declarations of the function runtime (text/plain): every global a function can call; a tenant's own /runtime.d.ts function route or site file is served instead", [
|
|
564
|
+
route("GET", "/runtime.d.ts", [], [], false),
|
|
565
|
+
]),
|
|
566
|
+
operation(618, "documentation.skill.get", "Read one agent skill by name (the route's {name}): its SKILL.md, frontmatter first. 404 when no such skill. No key needed", [
|
|
567
|
+
route("GET", "/v1/_docs/skills/{name}", ["name"], [], false),
|
|
568
|
+
]),
|
|
569
|
+
operation(617, "documentation.skill.list", "List every agent skill (name, title, what it does and when to use it); start with start-here. Read one with documentation.skill.get; substrio skills install writes them where your agent reads them. No key needed", [
|
|
570
|
+
route("GET", "/v1/_docs/skills", [], [], false),
|
|
571
|
+
]),
|
|
572
|
+
operation(488, "documentation.start", "Start here. Answers what this key can do on this app: the tenant, the key's scope, the live version number, every area of tools (functions, deploy, data, query, schema, schedule, files, config, access, observability, backup, ai, platform) with each tool's id, name and one-line summary, and the next steps of the build loop in order. Takes no input; read substrio://guide or GET /llms.txt for the long form", [
|
|
573
|
+
route("GET", "/v1/_start", [], [], false),
|
|
574
|
+
]),
|
|
575
|
+
operation(654, "documentation.tools", "The full definitions of one area's tools: the area is a name documentation.start lists (functions, deploy, data, query, schema, schedule, files, config, access, realtime, observability, backup, ai, account, platform). Answers each tool this key may call in that area as an MCP tool definition (name, title, description, inputSchema, outputSchema, annotations). The default tool list holds only the everyday tools; call any other by its own name, or through substrio_call", [
|
|
576
|
+
route("GET", "/v1/_start/{area}", ["area"], [], false),
|
|
577
|
+
]),
|
|
578
|
+
operation(7, "documentation.ui", "Read Swagger documentation UI", [
|
|
579
|
+
route("GET", "/v1/_docs", [], [], false),
|
|
580
|
+
]),
|
|
581
|
+
operation(42, "edge.domain.check", "Say whether a hostname is attached to a tenant", [
|
|
582
|
+
route("GET", "/v1/_edge/domain", [], ["domain"], false),
|
|
583
|
+
]),
|
|
584
|
+
operation(680, "env.host.list", "List the app's hosts, each with its environment and the version it runs, and the enabled root domains a host may be bound under", [
|
|
585
|
+
route("GET", "/v1/_env_hosts", [], [], false),
|
|
586
|
+
]),
|
|
587
|
+
operation(681, "env.host.remove", "Remove one of the app's hosts: it answers nothing from then on and its label is free. ENV_HOST_NOT_FOUND when the app holds no such host. Answers 204", [
|
|
588
|
+
route("DELETE", "/v1/_env_hosts/{host}", ["host"], [], false),
|
|
589
|
+
]),
|
|
590
|
+
operation(679, "env.host.set", "Bind a host of the app's choosing, <label>.<root> under an enabled root domain (my-app-v2.substrio.com), to one of its environments: live, or a sandbox (with version, a new sandbox running that version). The host answers exactly what that environment runs, with its data. A label has one owner across the platform: HOST_TAKEN when another app holds it, HOST_RESERVED for a reserved name or an app host, HOST_INVALID (with suggestion) for a label that is not a hostname label, ROOT_DOMAIN_UNKNOWN or ROOT_DOMAIN_DISABLED for the root. Binding a host the app holds again moves it. version.live.set with from: <host> pushes what it runs live", [
|
|
591
|
+
route("POST", "/v1/_env_hosts", [], [], true),
|
|
592
|
+
]),
|
|
593
|
+
operation(686, "featured.disable", "Deprecated: every account has every jet, always on, so it is refused FEATURED_ALWAYS_ON. A key stops with account.key.revoke; an app's keys go with the app", [
|
|
594
|
+
route("DELETE", "/v1/_accounts/{id}/featured/{jet}", ["id","jet"], [], false),
|
|
595
|
+
]),
|
|
596
|
+
operation(685, "featured.enable", "Deprecated: every account has every jet, always on. Answers the jet's entry as featured.list writes it and changes nothing", [
|
|
597
|
+
route("PUT", "/v1/_accounts/{id}/featured/{jet}", ["id","jet"], [], false),
|
|
598
|
+
]),
|
|
599
|
+
operation(687, "featured.key.create", "Deprecated: a jet key belongs to one of the account's apps, so it is refused JET_KEY_NEEDS_APP; jet.key.create makes it", [
|
|
600
|
+
route("POST", "/v1/_accounts/{id}/featured/{jet}/keys", ["id","jet"], [], true),
|
|
601
|
+
]),
|
|
602
|
+
operation(684, "featured.list", "List the featured apps (the jets: MapJet, SendJet, AuthJet and every later one) for the account: what each does, the plan it is metered against and the account's use this day and month (every app of the account summed) against that plan's limits. Every account has every jet: there is nothing to enable", [
|
|
603
|
+
route("GET", "/v1/_accounts/{id}/featured", ["id"], [], false),
|
|
604
|
+
]),
|
|
605
|
+
operation(688, "featured.set", "Feature an app for every account, or stop featuring it (platform admin). It must be open to other accounts' keys and its plans file must have a featured block (title, summary, admin). An account on a plan there keeps it when it stops being featured", [
|
|
606
|
+
route("PUT", "/v1/_featured/{jet}", ["jet"], [], true),
|
|
607
|
+
]),
|
|
608
|
+
operation(742, "file.adopt", "Platform operator only: store a file that is already on this server's own disk (under the server's --adopt-dir) at a path of the app the request names, with 0 bytes written: hashed in one read, then renamed in. Answers what file.upload answers", [
|
|
609
|
+
route("POST", "/v1/_files/adopt", [], [], true),
|
|
610
|
+
route("POST", "/v1/files/adopt", [], [], true),
|
|
611
|
+
]),
|
|
612
|
+
operation(239, "file.delete", "Delete one stored file by its path (the route's tail, as file.list shows it)", [
|
|
613
|
+
route("DELETE", "/v1/_files/{path}", ["path"], ["path"], false),
|
|
614
|
+
route("DELETE", "/v1/files/{path}", ["path"], ["path"], false),
|
|
615
|
+
]),
|
|
616
|
+
operation(238, "file.download", "Download one stored file by path (from file.list)", [
|
|
617
|
+
route("GET", "/v1/_files/{path}", ["path"], ["path"], false),
|
|
618
|
+
route("GET", "/v1/files/{path}", ["path"], ["path"], false),
|
|
619
|
+
]),
|
|
620
|
+
operation(236, "file.list", "List the app's stored files by path (optionally under a prefix): the path is what file.download and file.delete take", [
|
|
621
|
+
route("GET", "/v1/_files", [], ["limit","cursor","prefix"], false),
|
|
622
|
+
route("GET", "/v1/files", [], ["limit","cursor","prefix"], false),
|
|
623
|
+
]),
|
|
624
|
+
operation(783, "file.move", "Rename a stored file's path from one path to another, its content never copied. Refused with FILE_EXISTS when a file is at to, unless replace is true; FILE_NOT_FOUND when none is at from. Answers the file at its new path, as file.upload answers", [
|
|
625
|
+
route("POST", "/v1/_files/move", [], [], true),
|
|
626
|
+
route("POST", "/v1/files/move", [], [], true),
|
|
627
|
+
]),
|
|
628
|
+
operation(786, "file.public.get", "Read a stored file marked public, with no key, on the app's own hosts; a private or missing path is the same 404", [
|
|
629
|
+
route("GET", "/_files/{path}", ["path"], [], false),
|
|
630
|
+
]),
|
|
631
|
+
operation(453, "file.site_root.get", "Read the tenant site's index page (the app named by Host or X-Substrio-Tenant)", [
|
|
632
|
+
route("GET", "/", [], [], false),
|
|
633
|
+
]),
|
|
634
|
+
operation(333, "file.static_asset.get", "Read a tenant static asset", [
|
|
635
|
+
route("GET", "/_/{path}", ["path"], [], false),
|
|
636
|
+
]),
|
|
637
|
+
operation(782, "file.update", "Change a stored file's settings (public, content_type, cache_control) by its path, with no byte of it sent again: its bytes and hash are untouched and an absent setting keeps its value. Answers what file.upload answers; FILE_NOT_FOUND when no file is at the path", [
|
|
638
|
+
route("PATCH", "/v1/_files/{path}", ["path"], ["path"], true),
|
|
639
|
+
route("PATCH", "/v1/files/{path}", ["path"], ["path"], true),
|
|
640
|
+
]),
|
|
641
|
+
operation(237, "file.upload", "Upload a file into the app's file storage at its path; see the schema for the body. Answers its path, which file.download and file.delete take", [
|
|
642
|
+
route("POST", "/v1/_files", [], [], true),
|
|
643
|
+
route("POST", "/v1/files", [], [], true),
|
|
644
|
+
]),
|
|
645
|
+
operation(355, "function.catalog", "Summarize tenant databases and deployed functions", [
|
|
646
|
+
route("GET", "/v1/_functions/_catalog", [], ["max_databases","max_functions"], false),
|
|
647
|
+
]),
|
|
648
|
+
operation(483, "function.check", "Check a function before deploying it: the same body as function.deploy (script, name, routes, vars, bindings); parses the source as a script, CommonJS or ES module without running it and applies deploy's manifest and require() refusals; answers ok and diagnostics (severity, message, file, line, column); nothing is written or executed", [
|
|
649
|
+
route("POST", "/v1/_functions/_check", [], [], true),
|
|
650
|
+
]),
|
|
651
|
+
operation(316, "function.compat", "Invoke a function through the compatibility endpoint", [
|
|
652
|
+
route("GET", "/fn/{function}", ["function"], [], false),
|
|
653
|
+
route("POST", "/fn/{function}", ["function"], [], true),
|
|
654
|
+
route("PUT", "/fn/{function}", ["function"], [], true),
|
|
655
|
+
route("DELETE", "/fn/{function}", ["function"], [], false),
|
|
656
|
+
route("GET", "/fn/{function}/{path}", ["function","path"], [], false),
|
|
657
|
+
route("POST", "/fn/{function}/{path}", ["function","path"], [], true),
|
|
658
|
+
route("PUT", "/fn/{function}/{path}", ["function","path"], [], true),
|
|
659
|
+
route("DELETE", "/fn/{function}/{path}", ["function","path"], [], false),
|
|
660
|
+
route("GET", "/fn/{path}", ["path"], [], false),
|
|
661
|
+
route("POST", "/fn/{path}", ["path"], [], true),
|
|
662
|
+
route("PUT", "/fn/{path}", ["path"], [], true),
|
|
663
|
+
route("DELETE", "/fn/{path}", ["path"], [], false),
|
|
664
|
+
]),
|
|
665
|
+
operation(132, "function.delete", "Delete one function by name; answers {success, deleted}, 404 when no such function", [
|
|
666
|
+
route("DELETE", "/v1/functions/{name}", ["name"], [], false),
|
|
667
|
+
]),
|
|
668
|
+
operation(130, "function.deploy", "Deploy one function now, outside a version: body name and its source (required): script for JavaScript, or rust for a Rust function's source, which the server compiles; optional main, routes (like 'GET /orders/:id' or 'ANY /hooks/*'), vars, bindings and deploy_nodes. The source is compiled to a handler first; a source that does not compile, or that calls require(), is refused and nothing changes. On success it is live at once and the answer is the function's metadata (id, content_hash, routes, env_vars, bindings). To ship several functions together, or to try them first, use version.create, sandbox.create and version.live.set; function.check shows the problems without writing", [
|
|
669
|
+
route("POST", "/v1/functions", [], [], true),
|
|
670
|
+
route("POST", "/v1/deploy", [], [], true),
|
|
671
|
+
]),
|
|
672
|
+
operation(345, "function.environment.delete", "Delete one environment variable of a function: the route's {name} and {key}, optional body expected_hash (the content_hash you last read; a function that changed since is refused). Takes effect on the next call; answers the function's metadata", [
|
|
673
|
+
route("DELETE", "/v1/functions/{name}/env/{key}", ["name","key"], [], true),
|
|
674
|
+
]),
|
|
675
|
+
operation(343, "function.environment.list", "List one function's environment variables: answers {function, content_hash, environment (name to value), count}; 404 when no such function. The values are shown in full; use secret.list for secrets, which are never shown", [
|
|
676
|
+
route("GET", "/v1/functions/{name}/env", ["name"], [], false),
|
|
677
|
+
]),
|
|
678
|
+
operation(344, "function.environment.set", "Set one environment variable of a function: body key and value (strings, key not empty), optional expected_hash (the content_hash you last read; a function that changed since is refused). Takes effect on the next call; answers the function's metadata", [
|
|
679
|
+
route("PUT", "/v1/functions/{name}/env", ["name"], [], true),
|
|
680
|
+
]),
|
|
681
|
+
operation(473, "function.errors", "Read the errors the app's functions threw, newest first: each has an id (also a cursor), function, revision (the function's newest revision when it threw), request_id, timestamp, error name, message and the stack as frames (function, file, line, column). Options: function, request_id, revision (only errors of one deploy), since and until (Unix seconds), limit and cursor. Answers errors, count and next_cursor. Check the revision against version.log with the function path: a deploy that fixed a bug stops adding errors under the new revision", [
|
|
682
|
+
route("GET", "/v1/_function_errors", [], ["since","function","limit","cursor","request_id","until","revision"], false),
|
|
683
|
+
]),
|
|
684
|
+
operation(134, "function.eval", "Run a Lua script against the tenant's default key-value store, Redis style, in a build with the Redis frontend (not in this release, where the Redis frontend is a build switch, off, and the call answers NOT_IMPLEMENTED). Body script (or sha of a script loaded earlier with load:true, which answers {sha}), keys and args as arrays of strings. Answers {result}. This is Lua over key-value data, not JavaScript: to run JavaScript use function.invoke, route.call or function.execute_inline", [
|
|
685
|
+
route("POST", "/v1/eval", [], [], true),
|
|
686
|
+
]),
|
|
687
|
+
operation(256, "function.execute_inline", "Run inline JavaScript in the app's runtime: body code (required, not empty). It runs with the caller's own permissions through the same data API a function uses, and nothing is deployed or stored. For a deployed function use function.invoke; to check source without running it use function.check", [
|
|
688
|
+
route("POST", "/v1/_exec", [], [], true),
|
|
689
|
+
]),
|
|
690
|
+
operation(129, "function.get", "Read one deployed function: its name, its current source script and its metadata (content_hash, routes, env_vars, bindings, enabled, deploy_nodes). 404 when no such function. For a large source read line ranges with function.source.read instead", [
|
|
691
|
+
route("GET", "/v1/functions/{name}", ["name"], [], false),
|
|
692
|
+
]),
|
|
693
|
+
operation(336, "function.index", "List deployable functions and immutable source metadata", [
|
|
694
|
+
route("GET", "/v1/_functions/_index", [], [], false),
|
|
695
|
+
]),
|
|
696
|
+
operation(360, "function.invoke", "Invoke one deployed function with a JSON event", [
|
|
697
|
+
route("POST", "/v1/functions/{name}/_invoke", ["name"], ["timeout_ms","memory_mb"], true, "event"),
|
|
698
|
+
]),
|
|
699
|
+
operation(128, "function.list", "List the app's deployed functions with their metadata, paged: the limit and cursor options in, next_cursor out. function.catalog summarizes databases and functions together; route.list shows which routes answer", [
|
|
700
|
+
route("GET", "/v1/functions", [], ["limit","cursor"], false),
|
|
701
|
+
]),
|
|
702
|
+
operation(280, "function.logs", "Read the app's function logs: options function (id, tenant/name or name), level, request_id (every line of one call), since and until (Unix seconds), limit, and cursor to continue after a next_cursor; wait_ms with a cursor long-polls for new lines (at most 30000 ms). Answers logs, count, next_cursor and latest_cursor. A line records no function revision, so to tell what a deploy changed use function.errors with its revision option, or version.log with the function path", [
|
|
703
|
+
route("GET", "/v1/_logs", [], ["limit","skip","since","q","level","function","cursor","request_id","until","wait_ms"], false),
|
|
704
|
+
]),
|
|
705
|
+
operation(281, "function.logs.clear", "Purge the stored function log lines of the app; answers {success, purged}", [
|
|
706
|
+
route("DELETE", "/v1/_logs", [], [], false),
|
|
707
|
+
]),
|
|
708
|
+
operation(485, "function.logs.stream", "Tail function logs live over WebSocket: connect with ?function=, ?request_id=, ?level= (as function.logs) and ?cursor= (default: only new lines); the first frame is {type:connected,cursor}, then {type:logs,cursor,logs:[...]}; a slow socket gets {type:lagged,cursor} and continues from that cursor, or reconnect with it", [
|
|
709
|
+
route("GET", "/v1/_logs/ws", [], [], false),
|
|
710
|
+
]),
|
|
711
|
+
operation(356, "function.patch.approval.get", "Inspect one durable function patch approval", [
|
|
712
|
+
route("GET", "/v1/functions/{name}/approvals/{token}", ["name","token"], [], false),
|
|
713
|
+
]),
|
|
714
|
+
operation(357, "function.patch.approval.revoke", "Revoke one pending function patch approval", [
|
|
715
|
+
route("DELETE", "/v1/functions/{name}/approvals/{token}", ["name","token"], [], false),
|
|
716
|
+
]),
|
|
717
|
+
operation(338, "function.source.edit", "Replace one byte range or one exclusive line range of a function's source: body replacement, an approval_token from function.source.preview (required: preview the patch first), the range, and expected_hash (defaults to the current content_hash; a source that changed since is refused with 409). The edited source is compiled before it is stored, JavaScript or Rust (a Rust edit that does not compile is RUST_COMPILE_FAILED with rustc's diagnostics, and the function keeps serving its last source)", [
|
|
718
|
+
route("PATCH", "/v1/functions/{name}/source", ["name"], [], true),
|
|
719
|
+
]),
|
|
720
|
+
operation(352, "function.source.preview", "Preview and approve one bounded function source edit", [
|
|
721
|
+
route("POST", "/v1/functions/{name}/source/preview", ["name"], [], true),
|
|
722
|
+
]),
|
|
723
|
+
operation(349, "function.source.read", "Read a bounded line range from the current function source", [
|
|
724
|
+
route("GET", "/v1/functions/{name}/source", ["name"], ["start_line","end_line","max_bytes","expected_hash"], false),
|
|
725
|
+
]),
|
|
726
|
+
operation(337, "function.source.replace", "Replace one function's whole source with a compare-and-swap: body script for a JavaScript function or rust for a Rust one (required, compiled before it is stored; a replace keeps the function's language), optional expected_hash (the content_hash you last read; it defaults to the current one, and a source that changed since is refused with a conflict), main, routes, vars, bindings and deploy_nodes. Fields left out keep their values; an empty array clears. The function must exist (404). Answers the function's metadata", [
|
|
727
|
+
route("PUT", "/v1/functions/{name}/source", ["name"], [], true),
|
|
728
|
+
]),
|
|
729
|
+
operation(350, "function.source.search", "Search bounded source snippets in the current function", [
|
|
730
|
+
route("GET", "/v1/functions/{name}/search", ["name"], ["max_bytes","expected_hash","q","max_matches"], false),
|
|
731
|
+
]),
|
|
732
|
+
operation(351, "function.source.symbols", "List bounded declaration symbols from the current function", [
|
|
733
|
+
route("GET", "/v1/functions/{name}/symbols", ["name"], ["max_bytes","expected_hash","max_symbols"], false),
|
|
734
|
+
]),
|
|
735
|
+
operation(335, "function.syntax.check", "Validate JavaScript syntax without executing the source", [
|
|
736
|
+
route("POST", "/v1/_functions/_syntax", [], [], true),
|
|
737
|
+
]),
|
|
738
|
+
operation(133, "function.toggle", "Enable or disable one function without deleting it: body enabled (required boolean). Answers {success, enabled}, 404 when no such function. A disabled function is left out of route.list", [
|
|
739
|
+
route("POST", "/v1/functions/{name}/toggle", ["name"], [], false),
|
|
740
|
+
]),
|
|
741
|
+
operation(131, "function.update", "Replace one function's source: PATCH with the body function.deploy takes (script or rust is required); the name comes from the path and a name in the body is ignored. A source that does not compile to a handler is refused with 400 and nothing changes. On success it is live at once and the answer is the function's metadata. For a change to part of the source use function.source.edit", [
|
|
742
|
+
route("PATCH", "/v1/functions/{name}", ["name"], [], true),
|
|
743
|
+
]),
|
|
744
|
+
operation(115, "graph.analytics", "Run an analytics algorithm over one graph label (the route's {label}); see the schema for the algorithm and its inputs", [
|
|
745
|
+
route("GET", "/v1/_graph/{label}/analytics", ["label"], ["op","dir","a","b","nodes","node","lo","hi","k","src","dst","target","bucket_size","labels","iterations","damping","samples","decay","percentile","threshold","seeds","probability","simulations","pairs"], false),
|
|
746
|
+
route("POST", "/v1/_graph/{label}/analytics", ["label"], [], true),
|
|
747
|
+
]),
|
|
748
|
+
operation(265, "graph.degree", "Read the degree (edge count) of one node of a graph label: the route's {label} and {id}", [
|
|
749
|
+
route("GET", "/v1/_graph/{label}/degree/{id}", ["label","id"], [], false),
|
|
750
|
+
]),
|
|
751
|
+
operation(112, "graph.edge.read", "Read the edges of one graph label (the route's {label})", [
|
|
752
|
+
route("GET", "/v1/_graph/{label}/edges", ["label"], ["src","dst"], false),
|
|
753
|
+
route("POST", "/v1/_graph/{label}/edges/_check", ["label"], [], true),
|
|
754
|
+
]),
|
|
755
|
+
operation(448, "graph.edge.remove", "Remove edges of one graph label (the route's {label}); see the schema for the body", [
|
|
756
|
+
route("POST", "/v1/_graph/{label}/edges/_remove", ["label"], [], true),
|
|
757
|
+
]),
|
|
758
|
+
operation(113, "graph.edge.write", "Write edges of one graph label (the route's {label}); see the schema for the body", [
|
|
759
|
+
route("POST", "/v1/_graph/{label}/edges", ["label"], [], true),
|
|
760
|
+
]),
|
|
761
|
+
operation(264, "graph.label.list", "List the graph labels (edge types) that exist in the app", [
|
|
762
|
+
route("GET", "/v1/_graph", [], [], false),
|
|
763
|
+
]),
|
|
764
|
+
operation(114, "graph.neighbors", "Read the neighbors of one node of a graph label: the route's {label} and {id}", [
|
|
765
|
+
route("GET", "/v1/_graph/{label}/neighbors/{id}", ["label","id"], ["dir"], false),
|
|
766
|
+
route("POST", "/v1/_graph/{label}/neighbors/{id}", ["label","id"], [], true),
|
|
767
|
+
]),
|
|
768
|
+
operation(266, "graph.stats", "Read the statistics of one graph label (the route's {label})", [
|
|
769
|
+
route("GET", "/v1/_graph/{label}/stats", ["label"], [], false),
|
|
770
|
+
route("GET", "/v1/_graph/{label}", ["label"], [], false),
|
|
771
|
+
]),
|
|
772
|
+
operation(644, "host.add", "Add a host to the manifest: its address, SSH port and user, location (country and city), group settings (max_nodes, oversell_percent), allocation caps (max_tenants, enforced at placement; max_disk_bytes, max_memory_bytes, cores), labels and the SSH private key its orchestrator signs in with. The key is sealed to this server and never answered again; the host is answered with the key's fingerprint", [
|
|
773
|
+
route("POST", "/v1/_hosts", [], [], true),
|
|
774
|
+
]),
|
|
775
|
+
operation(659, "host.bootstrap", "Turn on zram swap with zstd on a host over its orchestrator's SSH (as root): the generator config and the swappiness first, the package if missing, the unit restarted until zram0 reads zstd. Refused with HOST_CAPABILITY_MISSING unless host.probe found own_kernel, systemd and zram_module. Answers 202 with the host as it stands; host.get then answers bootstrap (ok, not_possible, unreachable, failed), zram_active among the capabilities and zram_bytes", [
|
|
776
|
+
route("POST", "/v1/_hosts/{id}/bootstrap", ["id"], [], false),
|
|
777
|
+
]),
|
|
778
|
+
operation(646, "host.get", "Read one host by id: its state, location, group settings, labels and key fingerprint (never the key)", [
|
|
779
|
+
route("GET", "/v1/_hosts/{id}", ["id"], [], false),
|
|
780
|
+
]),
|
|
781
|
+
operation(645, "host.list", "List the hosts of the manifest in id order, each with its state, location, group settings, labels and key fingerprint (never the key)", [
|
|
782
|
+
route("GET", "/v1/_hosts", [], [], false),
|
|
783
|
+
]),
|
|
784
|
+
operation(776, "host.node.deploy", "Install a packed release on a host as a node (a new slot) or onto an existing node (its slot): the release streams over the host's SSH into the release's own installer, which preflights, installs the unit, health checks and rolls back. Refused before anything is copied with HOST_NOT_PROBED, HOST_NO_SYSTEMD, HOST_NGINX_PRESENT, RELEASE_NOT_FOUND, RELEASE_NOT_PACKED, RELEASE_ISA_UNKNOWN, HOST_CPU_MISSING (the detail names the features), HOST_NODE_LIMIT or NODE_BUSY. Answers 202 with the node as it stands; host.node.list then answers running, failed or rolled_back", [
|
|
785
|
+
route("POST", "/v1/_hosts/{id}/nodes", ["id"], [], true),
|
|
786
|
+
]),
|
|
787
|
+
operation(775, "host.node.list", "List the nodes installed on a host in slot order, each with its slot, port, state (deploying, running, failed, rolled_back, undeploying), the release last asked of it and when. A run that stays deploying for 30 minutes reads failed", [
|
|
788
|
+
route("GET", "/v1/_hosts/{id}/nodes", ["id"], [], false),
|
|
789
|
+
]),
|
|
790
|
+
operation(777, "host.node.undeploy", "Stop and remove a node's service and unit on a host; its data folder stays. Refused with HOST_HAS_APPS for the last node of a host while apps are placed on it and NODE_BUSY while its run is under way. Answers 202 with the node as it stands", [
|
|
791
|
+
route("DELETE", "/v1/_hosts/{id}/nodes/{slot}", ["id","slot"], [], false),
|
|
792
|
+
]),
|
|
793
|
+
operation(658, "host.probe", "Probe a host over its orchestrator's SSH, read only: whether it runs its own kernel (bare metal, kvm, another VM) or shares one (openvz, lxc, another container), systemd, the zram module, and zram swap active with zstd. Answers 202 with the host as it stands; the run lands on the host record in one transaction, and host.get answers virt, capabilities, probe (ok, unreachable, failed) and probed_at", [
|
|
794
|
+
route("POST", "/v1/_hosts/{id}/probe", ["id"], [], false),
|
|
795
|
+
]),
|
|
796
|
+
operation(648, "host.remove", "Remove a host and its sealed key from the manifest; refused with HOST_HAS_NODES while it carries nodes and HOST_HAS_APPS while apps are placed on it (tenant.place moves them). Answers 204 with no body", [
|
|
797
|
+
route("DELETE", "/v1/_hosts/{id}", ["id"], [], false),
|
|
798
|
+
]),
|
|
799
|
+
operation(666, "host.tenants", "List the apps placed on one host, in app id order, a page at a time (limit, cursor): data (each app's _id and slug), tenants and max_tenants (the host's maintained count and its cap), then next_cursor (null on the last page). One prefix scan of that host's placements; tenants and max_tenants on host.get are the maintained count and the cap", [
|
|
800
|
+
route("GET", "/v1/_hosts/{id}/tenants", ["id"], ["limit","cursor"], false),
|
|
801
|
+
]),
|
|
802
|
+
operation(647, "host.update", "Change a host: its state (available, draining, offline), address, SSH port and user, location, group settings or labels; a field left out keeps its value. An ssh_key rotates the key: the new one is sealed and the old one gone", [
|
|
803
|
+
route("PATCH", "/v1/_hosts/{id}", ["id"], [], true),
|
|
804
|
+
]),
|
|
805
|
+
operation(478, "index.create", "Create an index. kind sql (the default): on a SQL table: body table, name, columns (each name, optional descending and prefix_length for a text column), optional unique and if_not_exists; answers {success, table, name}; an unknown table or column, or a name already used, is refused with the engine's message, errno and sqlstate. kind expiry: body collection, field, after_ms and optional existing and if_not_exists; documents whose field holds a date are hidden from every read once the date plus after_ms has passed, and the collection's sweeper deletes them then (their keys, counters and unique values go with them; a change event and an oplog delete follow); a document without the field never expires; a stored value that is not a date refuses the declaration (existing allow declares anyway); a secret field is refused (400 SECRET_FIELD: a secret is in no key); the field is declared indexed; answers {success, kind, collection, field, after_ms, armed_ms, state ready}; a collection holds one expiry (409 unless if_not_exists); index.list kind expiry reads it and index.drop kind expiry takes it away. kind collection: declare a field of a collection: body collection, field, optional unique (true: a field or dotted path no two documents share; false, the default: indexed, its keys built and kept) and if_not_exists; the field's keys are built before the answer {success, kind, collection, field, unique, state ready} (the answer is the readiness signal); the declaration lasts across restarts, and a unique one makes every write path refuse a duplicate with UNIQUE (409); fields (an array, instead of field) declares a compound, unique as a tuple (UNIQUE then names each member in fields and values), and nulls count makes a null or missing member a value of its own (default skip: a document with no value for a member is not checked); within document makes the field unique inside each document (an array at the path may not hold a value twice; documents may share it) and within both does both (default collection; one field with nulls skip, else 400); the same fields with the other nulls are 409, a document holding several values (an array) of a member is 409 naming the member, nulls count on a nested path is 400; two documents already sharing a value refuse a unique declaration with UNIQUE and store nothing; a field already declared is 409 unless if_not_exists; index.drop kind collection takes it away. kind space declares a unique space: body space and members ([{collection, field}], at least one); the fields share one namespace, so no value may appear in two places of the space (two documents of one member, or one document of each of two members; a document holding a value in two members of its own collection is allowed), refused with UNIQUE (409) on insert, update, patch and batch, and the writers of one space are ordered so concurrent writes of one value let exactly one through; each member becomes a unique field of its collection (made when it does not exist) and cannot be dropped alone; existing documents holding a value in two places refuse the declaration with INDEX_DUPLICATES naming the collection; nulls, within and compound members are 400; a space declared with other members is 409 unless it is dropped first; answers {success, kind, space, members, state ready}; index.drop kind space takes it away. With keyword true (and optional keyword_term_bytes) the field is declared keyword instead (see keyword); answers {success, kind, collection, field, keyword, keyword_term_bytes, state ready}; index.drop with keyword true takes it away. kind aggregate declares a live count and sum per group (or over the whole collection with no group): body collection, name, group (0 or more field names or {field, step} bucket parts), metrics ({name: \"count\" or {sum: field}, ...}, at least one) and optional where (a declared segment's name: only its members are counted); every write keeps the group's count and sums current, and groupBy/sum/avg/count chains over the same shape answer from them with no record read; a read while it is Building answers by the exact scan instead; declaring the same name with the same shape again answers success, with another shape 400; answers {success, kind, collection, name, group, metrics, where, state}; index.drop kind aggregate takes it away (404 KEY_NOT_FOUND unless the name is declared); index.list kind aggregate lists every one with its state. kind vector makes a dense vector field's index: body collection, field, dims (17 to 4096) and optional metric (l2, the default, or cosine; dot is 400 naming TV9-DOT, the TurboVector9 step that brings it), bands, route and clusters (TurboVector9's code; default by the dims); dims, metric and code are fixed when it is made, so declaring the same shape again answers success and another (or a field whose first vector already made it with another) is 409 with the held shape; answers {success, kind, collection, field, dims, metric, bands, route, clusters, created, vectors, trained, settled} once a retrain the vectors written so far made due is done (10 minutes at most; settled false past that), so declaring again after a load is the readiness signal; a $knn query takes probes, target and budget (the search's clusters probed at most, neighbour mass covered and candidates reranked; default by k). index.list shows its state", [
|
|
806
|
+
route("POST", "/v1/_indexes", [], [], true),
|
|
807
|
+
]),
|
|
808
|
+
operation(479, "index.drop", "Drop one index of a SQL table: the route's {name} and the required table option. Answers {success, table, name}; index.list shows the indexes. kind=collection with the collection option drops a collection's declaration: {name} is the field (a compound: the fields option names its members), every declaration of that member set (unique and indexed) goes, its keys stay and writes are no longer checked; answers {success, kind, collection, field}, 404 KEY_NOT_FOUND when the field was not declared; with keyword=true it drops the field's keyword declaration instead: its terms, id and declaration go (answers {success, kind, collection, field, keyword}); with elements (a JSON array of the tuple's members) it drops the array field's element declaration instead: its keys, id and declaration go (answers {success, kind, collection, field, elements}, 404 KEY_NOT_FOUND when it was not declared). kind=space drops the unique space named by {name}: its members no longer share a namespace and stay plain unique fields of their collections; answers {success, kind, space, members}, 404 KEY_NOT_FOUND when there is no such space. kind=aggregate with the collection option drops the aggregate named by {name}: its declaration and group records go; answers {success, kind, collection, name}, 404 KEY_NOT_FOUND when it was not declared", [
|
|
809
|
+
route("DELETE", "/v1/_indexes/{name}", ["name"], ["table","kind","collection","keyword","fields","elements","compound"], false),
|
|
810
|
+
]),
|
|
811
|
+
operation(477, "index.list", "List the indexes of the SQL tables with each one's value-key state: state ready (every row has its key and every write keeps it), building (a first query is posting the keys now) or none (no key kept; reason says why; a failed build reports none and the engine retries it). Pass table for one table. A collection's field is keyed by its first query, or declared with index.create kind collection: pass kind collection with collection and field to read whether the field is declared (unique, or indexed: a unique field is indexed too) and its key state and the reason it has no key; without field it lists every field the collection declares. Pass kind conflicts with collection and field (or fields, a JSON array, for a compound, with nulls skip or count) to list what stops the field from being declared unique (a declaration over duplicates answers INDEX_DUPLICATES with the first and the count): {conflicts: [{_id, value}], count, next_cursor}, the documents that hold a value another document holds, in id order, paged with limit and cursor; it builds the field's keys when they are not kept yet. Pass kind space to list the unique spaces: {spaces: [{space, members: [{collection, field}]}], count}. Pass kind expiry with collection to read the collection's expiry declaration {field, after_ms, armed_ms}. A collection's keyword and element declarations are listed beside its fields, each its own entry: keyword true with keyword_term_bytes, or elements (the tuple's members), with state and reason. Pass kind aggregate with collection to list the collection's declared live aggregates (index.create kind aggregate): {name, group, metrics, where, state}", [
|
|
812
|
+
route("GET", "/v1/_indexes", [], ["limit","cursor","table","kind","collection","field","fields","nulls","within"], false),
|
|
813
|
+
]),
|
|
814
|
+
operation(784, "jet.key.create", "Make a key of one of the account's apps scoped to one jet; the secret is returned once. The jet sees each call as that app (request.auth.app) and meters it to the account's plan, the app's own use counted beside; removing the app revokes the key. With origins the key is publishable (safe in a page's source, answering only those origins). An unscoped account key may make one; a scoped key may not", [
|
|
815
|
+
route("POST", "/v1/_accounts/{id}/apps/{slug}/jets/{jet}/keys", ["id","slug","jet"], [], true),
|
|
816
|
+
]),
|
|
817
|
+
operation(781, "jet.list", "List the jets of one of the account's apps: each jet's menu in the app's dashboard (app_nav: its pages, each a list, form or detail over the jet's own routes), the account's plan and use there (every app summed: the limit is the account's) and this app's own use this day and month", [
|
|
818
|
+
route("GET", "/v1/_accounts/{id}/apps/{slug}/jets", ["id","slug"], [], false),
|
|
819
|
+
]),
|
|
820
|
+
operation(196, "job.cancel", "Cancel one running background job by id: answers the job; 404 when no such job, and refused when it already ended", [
|
|
821
|
+
route("POST", "/v1/_jobs/{id}/cancel", ["id"], [], false),
|
|
822
|
+
]),
|
|
823
|
+
operation(313, "job.get", "Read one background job by id: its status, progress and, for a backup, the backup id", [
|
|
824
|
+
route("GET", "/v1/_jobs/{id}", ["id"], [], false),
|
|
825
|
+
]),
|
|
826
|
+
operation(195, "job.list", "List the app's background jobs (re-embeds and other long operations) and their status and progress, oldest first, a page at a time (limit, cursor; next_cursor)", [
|
|
827
|
+
route("GET", "/v1/_jobs", [], ["limit","cursor"], false),
|
|
828
|
+
]),
|
|
829
|
+
operation(100, "kv.batch.delete", "Delete many keys of a key-value collection in one call: body collection and keys (at least one)", [
|
|
830
|
+
route("POST", "/v1/kv/{collection}/_del", ["collection"], [], true, "keys"),
|
|
831
|
+
]),
|
|
832
|
+
operation(99, "kv.batch.get", "Read many keys of a key-value collection in one call: body collection and keys (at least one)", [
|
|
833
|
+
route("POST", "/v1/kv/{collection}/_get", ["collection"], [], true, "keys"),
|
|
834
|
+
]),
|
|
835
|
+
operation(98, "kv.delete", "Delete keys from a key-value collection: body collection and keys (at least one)", [
|
|
836
|
+
route("DELETE", "/v1/kv/{collection}", ["collection"], [], true, "keys"),
|
|
837
|
+
]),
|
|
838
|
+
operation(96, "kv.get", "Read one key of a key-value collection: the route's {collection} and {key}", [
|
|
839
|
+
route("GET", "/v1/kv/{collection}/{key}", ["collection","key"], [], false),
|
|
840
|
+
]),
|
|
841
|
+
operation(101, "kv.scan", "Scan the keys of a key-value collection in order: options match (only keys with this prefix), count (page size, default 10) and cursor (resume after the previous page's cursor). Answers keys, count and cursor. Values are read with kv.get", [
|
|
842
|
+
route("GET", "/v1/kv/{collection}/_scan", ["collection"], ["cursor","match","count"], false),
|
|
843
|
+
]),
|
|
844
|
+
operation(97, "kv.set", "Write key-value entries into a collection: body collection and entries, a list of [key, value] string pairs (at least one)", [
|
|
845
|
+
route("PUT", "/v1/kv/{collection}", ["collection"], [], true, "entries"),
|
|
846
|
+
route("POST", "/v1/kv/{collection}", ["collection"], [], true, "entries"),
|
|
847
|
+
]),
|
|
848
|
+
operation(303, "kv.stream", "Stream binary key-value operations", [
|
|
849
|
+
route("GET", "/v1/kv/ws", [], [], false),
|
|
850
|
+
]),
|
|
851
|
+
operation(608, "ledger.accounts", "Read every account of one kind (the catalog name) in one currency: its pending and cleared amount summed over every organization, voided entries never count; a party with no entry of that kind in that currency is left out, never a zero entry. One walk of that kind's groups of the balance aggregate, no new index. Answers {accounts: [{party, amount}]}", [
|
|
852
|
+
route("GET", "/v1/_ledger/accounts", [], ["kind","currency"], false),
|
|
853
|
+
]),
|
|
854
|
+
operation(609, "ledger.accounts.totals", "Read every group of one account kind (the catalog name) in one currency kept by the party totals (the tenant first declares them with ledger.totals.declare by=party): each party's amount sum and row count by category, type and status, and first=true on the groups of the one live positive entry of each (kind, party, reference), whose rows count the references credited to that party. One walk of that kind's groups, no record read. Answers {totals: [{party, type, status, category?, first?, amount, rows}]}", [
|
|
855
|
+
route("GET", "/v1/_ledger/accounts/totals", [], ["kind","currency"], false),
|
|
856
|
+
]),
|
|
857
|
+
operation(593, "ledger.balance", "Read one (kind, party, currency) group's balance: pending and cleared amounts and row counts; voided entries never count", [
|
|
858
|
+
route("GET", "/v1/_ledger/balance", [], ["kind","party","currency","org"], false),
|
|
859
|
+
]),
|
|
860
|
+
operation(605, "ledger.balances.top", "Read the n (default 10, at most 1000) (kind, party) balances with the largest summed amount in one currency, largest first; a party with none of that currency is left out, never a zero entry. One walk of the balance aggregate's own groups, no new index. Answers {balances: [{account_kind, party, amount}]}", [
|
|
861
|
+
route("GET", "/v1/_ledger/balances/top", [], ["currency","limit"], false),
|
|
862
|
+
]),
|
|
863
|
+
operation(590, "ledger.catalog.get", "Read the tenant's ledger catalog: its declared account kind and category names, in index order", [
|
|
864
|
+
route("GET", "/v1/_ledger/catalog", [], [], false),
|
|
865
|
+
]),
|
|
866
|
+
operation(591, "ledger.catalog.set", "Declare the tenant's ledger catalog: kinds and categories, each the tenant's whole list in order, either omitted meaning that list stands. Append only: a list that is not a strict prefix of what's stored is refused (409) and changes nothing. Answers the catalog", [
|
|
867
|
+
route("POST", "/v1/_ledger/catalog", [], [], true),
|
|
868
|
+
]),
|
|
869
|
+
operation(599, "ledger.clear_due", "Clear every pending entry whose clear_at has passed, oldest first, at most limit entries (default 1000, at most 10000) in this call; the cutoff is always now, on the server, never caller supplied. Answers {cleared, more}: more is true when entries past the cutoff remain", [
|
|
870
|
+
route("POST", "/v1/_ledger/clear-due", [], [], true),
|
|
871
|
+
]),
|
|
872
|
+
operation(594, "ledger.entries", "List posted entries by one index (party, reference, scope or org) and its value, or by=all for the whole ledger (value absent), newest first. before pages older than the entry of that id (default: the newest); limit caps the page (default 50, at most 1000); org (by=party only) narrows a party's entries to one organization. Answers {entries, next_before}: next_before is the id to page from next, or 0 at the end", [
|
|
873
|
+
route("GET", "/v1/_ledger/entries", [], ["by","value","before","limit","org"], false),
|
|
874
|
+
]),
|
|
875
|
+
operation(604, "ledger.entry.annotate", "Set an administrator's note and tags on one entry by its id; never a money or index field. Answers {outcome: CHANGED}; an unknown id is refused (404)", [
|
|
876
|
+
route("POST", "/v1/_ledger/entries/{id}/annotate", ["id"], [], true),
|
|
877
|
+
]),
|
|
878
|
+
operation(596, "ledger.entry.clear", "Clear one pending entry by its id: moves it from pending to cleared. Answers {outcome: CHANGED or UNCHANGED}; an already cleared entry is UNCHANGED, a voided or reversal entry is refused (409)", [
|
|
879
|
+
route("POST", "/v1/_ledger/entries/{id}/clear", ["id"], [], false),
|
|
880
|
+
]),
|
|
881
|
+
operation(606, "ledger.entry.clear_at", "Set (clear_at > 0) or take away (clear_at == 0) one pending entry's own clear time by its id; never a money field. Answers {outcome: CHANGED or UNCHANGED}; a cleared or voided entry is refused (409); an unknown id is 404", [
|
|
882
|
+
route("POST", "/v1/_ledger/entries/{id}/clear-at", ["id"], [], true),
|
|
883
|
+
]),
|
|
884
|
+
operation(595, "ledger.entry.get", "Read one posted entry by its id (the route's {id}, as ledger.post or ledger.entries answered it)", [
|
|
885
|
+
route("GET", "/v1/_ledger/entries/{id}", ["id"], [], false),
|
|
886
|
+
]),
|
|
887
|
+
operation(598, "ledger.entry.reverse", "Post a REVERSAL entry that offsets one original entry by its id. Answers {outcome: POSTED or EXISTING, ids}; reversing the same original again answers EXISTING with the same id and writes nothing. A voided original, or an original that is itself a reversal, is refused (409); an unknown id is 404", [
|
|
888
|
+
route("POST", "/v1/_ledger/entries/{id}/reverse", ["id"], [], false),
|
|
889
|
+
]),
|
|
890
|
+
operation(597, "ledger.entry.void", "Void one pending entry by its id: it stops counting toward its balance. Answers {outcome: CHANGED or UNCHANGED}; an already voided entry is UNCHANGED, a cleared or reversal entry is refused (409)", [
|
|
891
|
+
route("POST", "/v1/_ledger/entries/{id}/void", ["id"], [], false),
|
|
892
|
+
]),
|
|
893
|
+
operation(600, "ledger.import", "Import 1 to 1000 ledger entries from an older system's own history, whole or not at all: each entry keeps its own created_at and its own id, and may already be VOIDED or a REVERSAL (naming the original it reverses by original_key). Answers {posted, existing}; an import of the same keys again answers every entry existing and writes nothing", [
|
|
894
|
+
route("POST", "/v1/_ledger/import", [], [], true),
|
|
895
|
+
]),
|
|
896
|
+
operation(592, "ledger.post", "Post 1 to 1000 ledger entries, whole or not at all: each entry's kind and category (when given) are the tenant's declared catalog names. balanced (default false) refuses the whole post unless every currency's amounts sum to zero. Answers {outcome: POSTED or EXISTING, ids}; a post of the same keys again answers EXISTING with the same ids and writes nothing", [
|
|
897
|
+
route("POST", "/v1/_ledger/entries", [], [], true),
|
|
898
|
+
]),
|
|
899
|
+
operation(601, "ledger.totals", "Read one scope's, one reference's or (all=true) the whole ledger's totals in one currency, maintained by account kind, category, type and status (the tenant must first declare them with ledger.totals.declare): each group's amount sum and row count; category is left out for the entries that named none. Exactly one of scope/reference/all", [
|
|
900
|
+
route("GET", "/v1/_ledger/totals", [], ["scope","reference","all","currency"], false),
|
|
901
|
+
]),
|
|
902
|
+
operation(602, "ledger.totals.declare", "Declare the tenant's scope, reference, all or party totals (idempotent, by defaults to scope): once declared, every post and status change keeps that totals current and ledger.totals reads them with no record read. Answers {outcome: DECLARED or EXISTING}", [
|
|
903
|
+
route("POST", "/v1/_ledger/totals/declare", [], [], true),
|
|
904
|
+
]),
|
|
905
|
+
operation(289, "live_view.create", "Create a live view; see the schema for its definition", [
|
|
906
|
+
route("POST", "/v1/_live_views", [], [], true),
|
|
907
|
+
]),
|
|
908
|
+
operation(291, "live_view.delete", "Delete one live view by its name (the route's {name}); live_view.list shows the live views", [
|
|
909
|
+
route("DELETE", "/v1/_live_views/{name}", ["name"], [], false),
|
|
910
|
+
]),
|
|
911
|
+
operation(290, "live_view.get", "Read the current data of one live view by name", [
|
|
912
|
+
route("GET", "/v1/_live_views/{name}/data", ["name"], [], false),
|
|
913
|
+
]),
|
|
914
|
+
operation(288, "live_view.list", "List the app's live views: their names are what live_view.get and live_view.delete take", [
|
|
915
|
+
route("GET", "/v1/_live_views", [], [], false),
|
|
916
|
+
]),
|
|
917
|
+
operation(292, "live_view.update", "Enable or disable one live view by name: body name and enabled", [
|
|
918
|
+
route("PATCH", "/v1/_live_views/{name}", ["name"], [], true),
|
|
919
|
+
]),
|
|
920
|
+
operation(515, "log_alert.create", "Add a log alert that publishes the app event log.alert (scope: the alert's id) when its condition first holds in a window, once per window; the webhooks that take log.alert (webhook.create with event log.alert) store, sign, retry and dead-letter it: error_count (function errors reach threshold), error_rate (errors over calls reach rate_percent, with min_calls calls) or log_match (log lines whose message holds every word of match, optionally of one function and level, reach threshold). Windows are fixed slots of unix time of window_ms. Answers the alert with its id", [
|
|
921
|
+
route("POST", "/v1/_log_alerts", [], [], true),
|
|
922
|
+
]),
|
|
923
|
+
operation(517, "log_alert.delete", "Delete one log alert by its id (the route's {id}, as log_alert.list shows it)", [
|
|
924
|
+
route("DELETE", "/v1/_log_alerts/{id}", ["id"], [], false),
|
|
925
|
+
]),
|
|
926
|
+
operation(514, "log_alert.list", "List the app's log alerts: each with its kind, filters, window, threshold, the window it last fired in, when, and how many webhooks took that notification", [
|
|
927
|
+
route("GET", "/v1/_log_alerts", [], [], false),
|
|
928
|
+
]),
|
|
929
|
+
operation(516, "log_alert.update", "Change one log alert by id: any of its settings; the window it last fired in is kept, so a change never notifies twice for one window", [
|
|
930
|
+
route("PATCH", "/v1/_log_alerts/{id}", ["id"], [], true),
|
|
931
|
+
]),
|
|
932
|
+
operation(511, "log_drain.create", "Add a log drain: every log line and every function error is POSTed, as NDJSON in batches, in order and at least once (a line carries its log_seq, so a receiver can drop a repeat), signed like a webhook when a secret is set. Answers the drain with its id. start now (default) delivers lines stored from now on, start delivers everything stored", [
|
|
933
|
+
route("POST", "/v1/_log_drains", [], [], true),
|
|
934
|
+
]),
|
|
935
|
+
operation(513, "log_drain.delete", "Delete one log drain by its id (the route's {id}, as log_drain.list shows it); its undelivered lines are not delivered", [
|
|
936
|
+
route("DELETE", "/v1/_log_drains/{id}", ["id"], [], false),
|
|
937
|
+
]),
|
|
938
|
+
operation(510, "log_drain.list", "List the app's log drains: each with its url, settings, acked (the cursor of the last line its endpoint accepted), latest (the newest stored line's cursor), the failures in a row and the endpoint's last HTTP status. A drain whose endpoint is down shows acked behind latest and catches up in order when the endpoint answers", [
|
|
939
|
+
route("GET", "/v1/_log_drains", [], [], false),
|
|
940
|
+
]),
|
|
941
|
+
operation(512, "log_drain.update", "Change one log drain by id: any of url, secret, headers, batch_lines, retry_ms, max_retry_ms, enabled; its progress is kept. A disabled drain stops delivering and resumes from its cursor when enabled", [
|
|
942
|
+
route("PATCH", "/v1/_log_drains/{id}", ["id"], [], true),
|
|
943
|
+
]),
|
|
944
|
+
operation(150, "mcp.rpc", "Serve the tenant's tools to AI agents over MCP (JSON-RPC, Streamable HTTP)", [
|
|
945
|
+
route("POST", "/mcp", [], [], true),
|
|
946
|
+
route("POST", "/v1/_mcp", [], [], true),
|
|
947
|
+
]),
|
|
948
|
+
operation(705, "mcp.tool.list", "List the app's own MCP tools (every route of its functions out of the box, mcp.json's, and those set with mcp.tool.set), each as tools/list shows it with its route, its source (route or manifest) and calls: the tools/call runs it has had", [
|
|
949
|
+
route("GET", "/v1/_mcp/tool", [], [], false),
|
|
950
|
+
]),
|
|
951
|
+
operation(707, "mcp.tool.remove", "Remove one tool (the route's {name}) that mcp.json or mcp.tool.set made, until a version goes live. A name the app's /mcp does not serve is MCP_TOOL_NOT_FOUND; a route's own tool is MCP_TOOL_FROM_ROUTE (its function says tools: {\"<route>\": false})", [
|
|
952
|
+
route("DELETE", "/v1/_mcp/tool/{name}", ["name"], [], false),
|
|
953
|
+
]),
|
|
954
|
+
operation(706, "mcp.tool.set", "Set one tool of the app's MCP manifest (the body; a tool of its name is replaced, any other added): its schema is checked first, and a refusal is MCP_TOOL_INVALID naming field. It lasts until a version goes live; declare it in functions/<name>.json or mcp.json to keep it", [
|
|
955
|
+
route("PUT", "/v1/_mcp/tool", [], [], true),
|
|
956
|
+
]),
|
|
957
|
+
operation(708, "mcp.tool.test", "Call one of the app's tools (the route's {name}) with arguments, as an end user (user) or as the caller's key, and answer exactly the tools/call result an agent would get. Counted as a call. Runs on the app's /mcp, where tools/call runs", [
|
|
958
|
+
route("POST", "/v1/_mcp/tool/{name}/test", ["name"], [], true),
|
|
959
|
+
]),
|
|
960
|
+
operation(153, "mcp.tools.delete", "Remove the app's MCP tool manifest, leaving the platform tools only", [
|
|
961
|
+
route("DELETE", "/v1/_mcp/tools", [], [], false),
|
|
962
|
+
]),
|
|
963
|
+
operation(151, "mcp.tools.get", "Read the app's own MCP tool manifest: the tools the app's /mcp serves besides the platform tools, and its instructions", [
|
|
964
|
+
route("GET", "/v1/_mcp/tools", [], [], false),
|
|
965
|
+
]),
|
|
966
|
+
operation(152, "mcp.tools.set", "Replace the app's MCP tool manifest (its own tools and instructions) with the body; see the schema. A version can carry the manifest too (version.create)", [
|
|
967
|
+
route("POST", "/v1/_mcp/tools", [], [], true),
|
|
968
|
+
]),
|
|
969
|
+
operation(641, "merge.abort", "Throw away a branch's merge draft (the route's {branch}). The branch itself never moved, so nothing of the app changes; the blobs the merge wrote are unreferenced and the next version.gc sweeps them. Answers {branch, deleted}", [
|
|
970
|
+
route("DELETE", "/v1/_merges/{branch}", ["branch"], [], false),
|
|
971
|
+
]),
|
|
972
|
+
operation(640, "merge.commit", "Publish a branch's merge draft (the route's {branch}): one commit of the merged units onto the branch, with the branch merged in as its second parent, and the draft is dropped. 409 while a unit is still conflicted or when the branch moved under the draft. A fast forward moves the branch instead of committing, and a branch already holding the other side's work is answered as up to date. Answers {branch, version, root, number, generation, committed, fast_forward, up_to_date}", [
|
|
973
|
+
route("POST", "/v1/_merges/{branch}/commit", ["branch"], [], true),
|
|
974
|
+
]),
|
|
975
|
+
operation(638, "merge.get", "The merge draft of a branch (the route's {branch}) as merge.plan and merge.resolve left it: both sides, the version they descend from, every unit the merge touched, what it did with each one and how many are still conflicted. 404 when the branch has no draft", [
|
|
976
|
+
route("GET", "/v1/_merges/{branch}", ["branch"], [], false),
|
|
977
|
+
]),
|
|
978
|
+
operation(637, "merge.plan", "Work out the merge of one branch into another: from names the branch (or version) whose work comes in, into the branch it lands on (default main). Three way against the version both descend from, so a unit only one side changed is kept without a question and a unit both changed is merged by lines. Publishes nothing: the plan is into's merge draft, which merge.resolve settles and merge.commit publishes. Answers {into, from, base, ours, theirs, up_to_date, fast_forward, conflicts, clean, units, count}", [
|
|
979
|
+
route("POST", "/v1/_merges", [], [], true),
|
|
980
|
+
]),
|
|
981
|
+
operation(639, "merge.resolve", "Settle one conflicted unit of a branch's merge draft (the route's {branch}) with the bytes it should hold, as text or as base64. The unit becomes resolved and the draft's conflict count falls by one, durably. Answers the whole draft again, so the next conflict is in hand", [
|
|
982
|
+
route("POST", "/v1/_merges/{branch}/resolve", ["branch"], [], true),
|
|
983
|
+
]),
|
|
984
|
+
operation(247, "mesh.drop", "Remove a tenant from selected mesh nodes", [
|
|
985
|
+
route("POST", "/v1/_mesh/drop", [], [], true),
|
|
986
|
+
]),
|
|
987
|
+
operation(248, "mesh.invoke", "Invoke a cloud function on the mesh", [
|
|
988
|
+
route("POST", "/v1/_mesh/invoke", [], [], true),
|
|
989
|
+
]),
|
|
990
|
+
operation(246, "mesh.push", "Provision a tenant on selected mesh nodes", [
|
|
991
|
+
route("POST", "/v1/_mesh/push", [], [], true),
|
|
992
|
+
]),
|
|
993
|
+
operation(300, "mesh.scatter", "Scatter a typed query across mesh nodes and tenants: each tenant answers every match unless query.limit sets its own limit; the merge orders by the query's first sort key, then keeps the top-level limit rows, or every row without one", [
|
|
994
|
+
route("POST", "/v1/_mesh/scatter", [], [], true),
|
|
995
|
+
]),
|
|
996
|
+
operation(244, "mesh.status", "Read mesh status", [
|
|
997
|
+
route("GET", "/v1/_mesh/status", [], [], false),
|
|
998
|
+
]),
|
|
999
|
+
operation(245, "mesh.tenants", "Enumerate mesh tenants", [
|
|
1000
|
+
route("GET", "/v1/_mesh/tenants", [], [], false),
|
|
1001
|
+
]),
|
|
1002
|
+
operation(254, "meta.gc", "Inspect or reclaim the app database's deferred-free space. GET answers limbo_entries, allocated_bytes and file_size_bytes; POST drains the deferred frees into the allocator and answers {triggered: true}. It frees space for reuse and never deletes data, and the file does not shrink. Pick the method with http_method", [
|
|
1003
|
+
route("GET", "/v1/_meta/gc", [], [], false),
|
|
1004
|
+
route("POST", "/v1/_meta/gc", [], [], false),
|
|
1005
|
+
]),
|
|
1006
|
+
operation(55, "namespace.delete", "Delete one namespace of a collection by name (the route's {collection} and {name})", [
|
|
1007
|
+
route("DELETE", "/v1/{collection}/_namespaces/{name}", ["collection","name"], [], false),
|
|
1008
|
+
]),
|
|
1009
|
+
operation(54, "namespace.list", "List the namespaces of one collection (the route's {collection}); namespace.stats reads one", [
|
|
1010
|
+
route("GET", "/v1/{collection}/_namespaces", ["collection"], [], false),
|
|
1011
|
+
]),
|
|
1012
|
+
operation(320, "namespace.stats", "Read the statistics of one namespace of a collection (the route's {collection} and {name})", [
|
|
1013
|
+
route("GET", "/v1/{collection}/_namespaces/{name}/stats", ["collection","name"], [], false),
|
|
1014
|
+
]),
|
|
1015
|
+
operation(651, "node_label.delete", "Delete a node label by name; refused with NODE_LABEL_IN_USE while a host carries it. Answers 204 with no body", [
|
|
1016
|
+
route("DELETE", "/v1/_node_labels/{name}", ["name"], [], false),
|
|
1017
|
+
]),
|
|
1018
|
+
operation(650, "node_label.list", "List the node labels in id order, each with its name and description", [
|
|
1019
|
+
route("GET", "/v1/_node_labels", [], [], false),
|
|
1020
|
+
]),
|
|
1021
|
+
operation(649, "node_label.set", "Define a node label (a hardware label a host carries, such as nvme or gpu), or change the description of the one with this name. 64 labels exist at most. Labels are not tiers and gate nothing", [
|
|
1022
|
+
route("PUT", "/v1/_node_labels/{name}", ["name"], [], true),
|
|
1023
|
+
]),
|
|
1024
|
+
operation(458, "oauth.authorize", "Start an authorization code request with PKCE and redirect to the consent page", [
|
|
1025
|
+
route("GET", "/oauth/authorize", [], [], false),
|
|
1026
|
+
]),
|
|
1027
|
+
operation(459, "oauth.consent_page", "Read the page a person approves or denies an authorization request on (text/html)", [
|
|
1028
|
+
route("GET", "/oauth/consent", [], [], false),
|
|
1029
|
+
]),
|
|
1030
|
+
operation(457, "oauth.register", "Register an OAuth client dynamically (RFC 7591)", [
|
|
1031
|
+
route("POST", "/oauth/register", [], [], true),
|
|
1032
|
+
]),
|
|
1033
|
+
operation(461, "oauth.request.decide", "Approve or deny a pending authorization request as the signed-in person", [
|
|
1034
|
+
route("POST", "/oauth/requests/{id}", ["id"], [], true),
|
|
1035
|
+
]),
|
|
1036
|
+
operation(460, "oauth.request.get", "Read what a pending authorization request asks for", [
|
|
1037
|
+
route("GET", "/oauth/requests/{id}", ["id"], [], false),
|
|
1038
|
+
]),
|
|
1039
|
+
operation(455, "oauth.resource_metadata", "Read the OAuth protected resource metadata of the tenant or of one MCP endpoint (RFC 9728)", [
|
|
1040
|
+
route("GET", "/.well-known/oauth-protected-resource", [], [], false),
|
|
1041
|
+
route("GET", "/.well-known/oauth-protected-resource/mcp", [], [], false),
|
|
1042
|
+
route("GET", "/.well-known/oauth-protected-resource/v1/_mcp", [], [], false),
|
|
1043
|
+
]),
|
|
1044
|
+
operation(463, "oauth.revoke", "Revoke an access or refresh token (RFC 7009, form-encoded)", [
|
|
1045
|
+
route("POST", "/oauth/revoke", [], [], true),
|
|
1046
|
+
]),
|
|
1047
|
+
operation(456, "oauth.server_metadata", "Read the tenant's OAuth authorization server metadata (RFC 8414)", [
|
|
1048
|
+
route("GET", "/.well-known/oauth-authorization-server", [], [], false),
|
|
1049
|
+
]),
|
|
1050
|
+
operation(462, "oauth.token", "Exchange an authorization code or a refresh token for an access token (form-encoded)", [
|
|
1051
|
+
route("POST", "/oauth/token", [], [], true),
|
|
1052
|
+
]),
|
|
1053
|
+
operation(500, "object.socket", "Open a WebSocket to a durable object: connect with ?class= (an exported class of the app) and ?name= (idFromName) or ?id= (32 hex digits). The object's fetch gets the upgrade request and accepts the socket (state.acceptWebSocket) or the socket is closed; each client frame is a webSocketMessage turn of the object, in order, and the client's close is a webSocketClose turn. A reader that falls behind the backlog bound is closed with 4290 after the frames queued before it", [
|
|
1054
|
+
route("GET", "/v1/_objects/ws", [], [], false),
|
|
1055
|
+
]),
|
|
1056
|
+
operation(451, "observability.loops", "Read the listen balance and the connections opened on each HTTP event loop and frontend worker since start", [
|
|
1057
|
+
route("GET", "/v1/_loops", [], [], false),
|
|
1058
|
+
]),
|
|
1059
|
+
operation(3, "observability.metrics", "Read process metrics", [
|
|
1060
|
+
route("GET", "/v1/_metrics", [], ["range"], false),
|
|
1061
|
+
]),
|
|
1062
|
+
operation(452, "observability.request_costs", "Read the request cost counters summed over every thread, and the function stage timers while they are on", [
|
|
1063
|
+
route("GET", "/v1/_request_costs", [], ["stages"], false),
|
|
1064
|
+
]),
|
|
1065
|
+
operation(466, "oidc.authorize", "Start an OpenID Connect sign-in request and redirect to the sign-in page", [
|
|
1066
|
+
route("GET", "/oidc/authorize", [], [], false),
|
|
1067
|
+
]),
|
|
1068
|
+
operation(464, "oidc.discovery", "Read the OpenID Connect provider's discovery document", [
|
|
1069
|
+
route("GET", "/.well-known/openid-configuration", [], [], false),
|
|
1070
|
+
]),
|
|
1071
|
+
operation(465, "oidc.jwks", "Read the keys that verify the provider's ID tokens (JWKS)", [
|
|
1072
|
+
route("GET", "/oidc/jwks", [], [], false),
|
|
1073
|
+
]),
|
|
1074
|
+
operation(469, "oidc.request.decide", "Approve or deny a pending sign-in request as the signed-in person", [
|
|
1075
|
+
route("POST", "/oidc/requests/{id}", ["id"], [], true),
|
|
1076
|
+
]),
|
|
1077
|
+
operation(468, "oidc.request.get", "Read what a pending sign-in request asks for", [
|
|
1078
|
+
route("GET", "/oidc/requests/{id}", ["id"], [], false),
|
|
1079
|
+
]),
|
|
1080
|
+
operation(467, "oidc.signin_page", "Read the page a person signs in and consents on (text/html)", [
|
|
1081
|
+
route("GET", "/oidc/signin", [], [], false),
|
|
1082
|
+
]),
|
|
1083
|
+
operation(470, "oidc.token", "Exchange an authorization code for an ID token and an access token (form-encoded)", [
|
|
1084
|
+
route("POST", "/oidc/token", [], [], true),
|
|
1085
|
+
]),
|
|
1086
|
+
operation(471, "oidc.userinfo", "Read the claims of the person an access token was issued for", [
|
|
1087
|
+
route("GET", "/oidc/userinfo", [], [], false),
|
|
1088
|
+
]),
|
|
1089
|
+
operation(682, "package.add", "Add an npm package and everything it depends on to a branch in one commit, under node_modules/<name>/, fetched from the registry the platform setting package_registry_origin names: each tarball's SHA-512 checked against dist.integrity, only ES module packages taken, one version of each name (a dependency the branch already holds is reused when it meets the range, else PACKAGE_VERSION_CONFLICT). A function then imports it by name. Answers {branch, version, number, packages: [{name, version}]}", [
|
|
1090
|
+
route("POST", "/v1/_packages", [], [], true),
|
|
1091
|
+
]),
|
|
1092
|
+
operation(683, "package.remove", "Take one package out of a branch in one commit: node_modules/<name>/ is removed (its dependencies stay until removed by name). Answers {branch, version, number, name}", [
|
|
1093
|
+
route("POST", "/v1/_packages/remove", [], [], true),
|
|
1094
|
+
]),
|
|
1095
|
+
operation(584, "permissions.get", "Read the tenant's declared permission catalog: each name, its bit and its closure (the names it implies, itself excluded), and the all name, if one closes to every bit, or null", [
|
|
1096
|
+
route("GET", "/v1/_permissions", [], [], false),
|
|
1097
|
+
]),
|
|
1098
|
+
operation(585, "permissions.set", "Declare the tenant's permission catalog: body names, all, implies; append only against the stored names (a reorder or rename is refused, naming the position). A deploy without this table changes nothing. Answers the stored catalog", [
|
|
1099
|
+
route("POST", "/v1/_permissions", [], [], true),
|
|
1100
|
+
]),
|
|
1101
|
+
operation(442, "plans.account.get", "Read the plan an account is on, with its limits and features; an account with no plan set is on the default plan", [
|
|
1102
|
+
route("GET", "/v1/_plans/accounts/{account}", ["account"], [], false),
|
|
1103
|
+
]),
|
|
1104
|
+
operation(443, "plans.account.set", "Put an account on one of the tenant's plans", [
|
|
1105
|
+
route("PUT", "/v1/_plans/accounts/{account}", ["account"], [], true),
|
|
1106
|
+
]),
|
|
1107
|
+
operation(746, "plans.catalog", "Read every plan sold, with no account and no person data: the Substrio plans (each with the jet plan it bundles per jet) and every featured app's (jet's) own plans, each with its limits and standalone price, each plans file as plans.get answers it. The sites build their prices from this. No key needed", [
|
|
1108
|
+
route("GET", "/v1/_billing/catalog", [], [], false),
|
|
1109
|
+
]),
|
|
1110
|
+
operation(441, "plans.delete", "Delete the app's plans file: every plan definition is gone and cannot be restored by this call (keep the plans.get answer to put it back with plans.set). Answers 204 with no body; accounts keep no plan and fall back to the default", [
|
|
1111
|
+
route("DELETE", "/v1/_plans", [], [], false),
|
|
1112
|
+
]),
|
|
1113
|
+
operation(439, "plans.get", "Read the app's plans file: the plans an account can be put on, each with its limits and features. Answers 404 when no plans are set, so a first plans.set is a create, not an update", [
|
|
1114
|
+
route("GET", "/v1/_plans", [], [], false),
|
|
1115
|
+
]),
|
|
1116
|
+
operation(440, "plans.set", "Replace the tenant's plans file; a bad file is refused and the deployed plans stay in force", [
|
|
1117
|
+
route("POST", "/v1/_plans", [], [], true),
|
|
1118
|
+
]),
|
|
1119
|
+
operation(563, "platform.audit.list", "Read the server-wide audit trail (tenant create, update and delete, platform settings, shared providers, key rotations), newest first; the filters, limit and cursor of audit.list", [
|
|
1120
|
+
route("GET", "/v1/_platform/audit", [], ["order","since","limit","cursor","until","action","actor_kind","actor","target_kind","target","outcome"], false),
|
|
1121
|
+
]),
|
|
1122
|
+
operation(741, "platform.counts", "Read the account and host counts for the operator, each kept with the accounts and hosts it counts", [
|
|
1123
|
+
route("GET", "/v1/_platform/counts", [], [], false),
|
|
1124
|
+
]),
|
|
1125
|
+
operation(436, "platform.setting.set", "Set one platform setting on the running server; it takes effect at once, is kept in the system database and wins over substrio.conf. The server-wide runtime limits (jsc.*, queue.*, objects.*) are set here; an empty value unsets one, restoring its default", [
|
|
1126
|
+
route("PUT", "/v1/_platform/settings/{key}", ["key"], [], true),
|
|
1127
|
+
]),
|
|
1128
|
+
operation(435, "platform.settings.get", "Read the platform settings: each one's kind, live value and meaning", [
|
|
1129
|
+
route("GET", "/v1/_platform/settings", [], [], false),
|
|
1130
|
+
]),
|
|
1131
|
+
operation(740, "platform.usage.history", "Read the KPI history of every app on this server for the operator, oldest first: one point per finished minute (step=minute, the default) or hour (step=hour), each with what every app's API requests (total, status classes 2xx to 5xx, latency buckets and percentiles) and function calls added in it, summed as each app's minute is written. since and until are Unix seconds (default: the last hour of minutes, or the last two days of hours); limit caps the points. Counted on the node that answers; platform calls (no app) are not counted", [
|
|
1132
|
+
route("GET", "/v1/_platform/usage/history", [], ["limit","since","until","step"], false),
|
|
1133
|
+
]),
|
|
1134
|
+
operation(82, "query.aggregate", "Run an aggregation pipeline over one collection: stages run in order, one operator per stage (see the schema); use_index false forces a scan. For SQL-style grouping with having use query.chain", [
|
|
1135
|
+
route("POST", "/v1/{collection}/_aggregate", ["collection"], ["use_index"], true),
|
|
1136
|
+
]),
|
|
1137
|
+
operation(476, "query.chain", "Run a typed chain query over a collection: the JS chain's methods as JSON (conditions eq, neq, gt, gte, lt, lte, in, nin, iin, ieq, exists, startsWith, istartsWith, endsWith, contains, icontains (served by a declared keyword index), like, ilike, regex, iregex, size, sizeGt, sizeLt, containsAll, containsAny, between, eqField and the other field comparisons (value names the other field), inQuery, notInQuery, existsQuery, and, or, not; elemMatch is JS only; joins of kind first, inner or left; sort keys, skip, limit, select, distinct; group_by with aggregates and having; compound union, unionAll, intersect, except; terminals all, first, count, ids, sum, avg, min, max). A field may be alias.field of a join. A grouped, aggregated, distinct or compound plan answers plain rows as sql does (rows, columns)", [
|
|
1138
|
+
route("POST", "/v1/_chain", [], [], true),
|
|
1139
|
+
]),
|
|
1140
|
+
operation(81, "query.count", "Count the records of a collection that match a where filter, without returning them; use_index false forces a scan", [
|
|
1141
|
+
route("GET", "/v1/{collection}/_count", ["collection"], ["use_index","where"], false),
|
|
1142
|
+
]),
|
|
1143
|
+
operation(83, "query.multi", "Run several queries over different collections in one call and answer each result in order", [
|
|
1144
|
+
route("POST", "/v1/_multi_search", [], [], true),
|
|
1145
|
+
]),
|
|
1146
|
+
operation(80, "query.qx", "Execute a parser-owned query plan", [
|
|
1147
|
+
route("POST", "/v1/{collection}/_qx", ["collection"], [], true),
|
|
1148
|
+
]),
|
|
1149
|
+
operation(450, "query.sql", "Run one SQL statement with bound parameters", [
|
|
1150
|
+
route("POST", "/v1/_sql", [], [], true),
|
|
1151
|
+
]),
|
|
1152
|
+
operation(301, "query.stream", "Stream typed query results over WebSocket", [
|
|
1153
|
+
route("GET", "/v1/query/ws", [], ["collection","where","select","limit","skip","sort"], false),
|
|
1154
|
+
]),
|
|
1155
|
+
operation(494, "queue.dead_letters", "Page a queue's dead letters, oldest first: messages that failed more than max_retries times and have no dead_letter_queue; each has its seq, attempts and body. Page with limit and cursor (the answer's next)", [
|
|
1156
|
+
route("GET", "/v1/_queues/{id}/dead", ["id"], ["limit","cursor"], false),
|
|
1157
|
+
]),
|
|
1158
|
+
operation(491, "queue.list", "List the app's durable queues (declared in the version's queues): name, consumer function, dead_letter_queue and every setting in effect. queue.stats counts a queue's messages", [
|
|
1159
|
+
route("GET", "/v1/_queues", [], [], false),
|
|
1160
|
+
]),
|
|
1161
|
+
operation(493, "queue.purge", "Remove a queue's messages for good: scope pending (default: waiting, delayed and in-flight), dead (the dead letters) or all. Answers how many were removed; 404 when the queue is not declared", [
|
|
1162
|
+
route("POST", "/v1/_queues/{id}/purge", ["id"], [], true),
|
|
1163
|
+
]),
|
|
1164
|
+
operation(495, "queue.retry_dead", "Send dead letters back to their queue with their attempts reset: one by seq, or every dead message when seq is absent. Answers how many were retried", [
|
|
1165
|
+
route("POST", "/v1/_queues/{id}/retry-dead", ["id"], [], true),
|
|
1166
|
+
]),
|
|
1167
|
+
operation(492, "queue.stats", "Count one queue's messages: ready (deliverable now), in_flight (delivered, visibility timeout not over), delayed (send delay or retry backoff) and dead (past max_retries); 404 when the queue is not declared", [
|
|
1168
|
+
route("GET", "/v1/_queues/{id}/stats", ["id"], [], false),
|
|
1169
|
+
]),
|
|
1170
|
+
operation(324, "rag.collection.delete", "Delete a collection's retrieval (RAG) configuration", [
|
|
1171
|
+
route("DELETE", "/v1/{collection}/_rag", ["collection"], [], false),
|
|
1172
|
+
]),
|
|
1173
|
+
operation(274, "rag.collection.get", "Read a collection's retrieval (RAG) configuration", [
|
|
1174
|
+
route("GET", "/v1/{collection}/_rag", ["collection"], [], false),
|
|
1175
|
+
]),
|
|
1176
|
+
operation(275, "rag.collection.set", "Write a collection's retrieval (RAG) configuration; see the schema", [
|
|
1177
|
+
route("PUT", "/v1/{collection}/_rag", ["collection"], [], false),
|
|
1178
|
+
route("POST", "/v1/{collection}/_rag", ["collection"], [], false),
|
|
1179
|
+
]),
|
|
1180
|
+
operation(276, "rag.drift", "Inspect embedding drift for a collection", [
|
|
1181
|
+
route("GET", "/v1/{collection}/_rag/drift", ["collection"], [], false),
|
|
1182
|
+
]),
|
|
1183
|
+
operation(277, "rag.embed", "Embed the records of a collection, using the collection's RAG configuration", [
|
|
1184
|
+
route("POST", "/v1/{collection}/_rag/embed", ["collection"], [], false),
|
|
1185
|
+
]),
|
|
1186
|
+
operation(242, "rag.get", "Read tenant RAG configuration", [
|
|
1187
|
+
route("GET", "/v1/_rag", [], [], false),
|
|
1188
|
+
]),
|
|
1189
|
+
operation(278, "rag.reembed", "Embed every record of a collection again, using the collection's RAG configuration", [
|
|
1190
|
+
route("POST", "/v1/{collection}/_rag/re-embed", ["collection"], [], false),
|
|
1191
|
+
]),
|
|
1192
|
+
operation(299, "rate_limit.delete", "Delete one rate-limit rule by its id, as rate_limit.list returned it: the limit stops applying at once and the rule cannot be restored, only added again with rate_limit.set. Answers 404 for an unknown id", [
|
|
1193
|
+
route("DELETE", "/v1/_ratelimit/{id}", ["id"], [], false),
|
|
1194
|
+
]),
|
|
1195
|
+
operation(297, "rate_limit.list", "List the app's rate-limit rules, each with its id (the id rate_limit.delete takes), path, method, key mode, max_requests and window_seconds", [
|
|
1196
|
+
route("GET", "/v1/_ratelimit", [], [], false),
|
|
1197
|
+
]),
|
|
1198
|
+
operation(298, "rate_limit.set", "Add a rate-limit rule: at most max_requests per window_seconds on the operations and the app's own function routes a path pattern and method name, counted per ip, token or tenant (by). Answers {id, success, matches: {operations, routes}}: what the pattern covers now (a pattern that covers nothing yet is accepted and limits a function route once it is deployed); a path that is neither * nor starts with / is 400, and a rule that is invalid or a duplicate of an existing one 409, so this adds and never updates one in place: delete the old rule by id first. Over a rule a request answers 429 with Retry-After; the master key is exempt", [
|
|
1199
|
+
route("POST", "/v1/_ratelimit", [], [], true),
|
|
1200
|
+
]),
|
|
1201
|
+
operation(208, "realtime.stats", "Read realtime state", [
|
|
1202
|
+
route("GET", "/v1/realtime/stats", [], [], false),
|
|
1203
|
+
]),
|
|
1204
|
+
operation(302, "realtime.stream", "Subscribe to typed realtime events", [
|
|
1205
|
+
route("GET", "/v1/realtime", [], [], false),
|
|
1206
|
+
]),
|
|
1207
|
+
operation(630, "ref.log", "Every move of one name, newest first (the route's {kind}, one of branch, tag or environment, and {name}): the version it moved from, the version it moved to, who moved it, when, and why (create, commit, merge, promote, rollback, restore, delete, protect, import). The log of a deleted name is kept, which is what ref.restore reads. 404 when the name has no log", [
|
|
1208
|
+
route("GET", "/v1/_refs/{kind}/{name}/log", ["kind","name"], ["limit"], false),
|
|
1209
|
+
]),
|
|
1210
|
+
operation(631, "ref.restore", "Put a name back to a version its own log holds (the route's {kind} and {name}): name the version, or the log entry by sequence. A name that was deleted is made again, and one that still lives is moved, which is itself one more log entry. A tag is never moved, only made again at the version it marked. Answers {name, version, previous}", [
|
|
1211
|
+
route("POST", "/v1/_refs/{kind}/{name}/restore", ["kind","name"], [], true),
|
|
1212
|
+
]),
|
|
1213
|
+
operation(774, "region.choices", "List the regions an app can be created in: those that are available and serving, each with its slug, public name and country. Empty until a region serves. Pass a slug as region to account.app.create to place the app there", [
|
|
1214
|
+
route("GET", "/v1/_accounts/{id}/regions", ["id"], [], false),
|
|
1215
|
+
]),
|
|
1216
|
+
operation(771, "region.list", "List the regions in id order, each with its slug, public name, country, state (available or hidden) and whether it is serving (answers app traffic; the operator cannot set that)", [
|
|
1217
|
+
route("GET", "/v1/_regions", [], [], false),
|
|
1218
|
+
]),
|
|
1219
|
+
operation(773, "region.remove", "Remove a region by slug; refused with REGION_IN_USE while a host names it. Answers 204 with no body", [
|
|
1220
|
+
route("DELETE", "/v1/_regions/{slug}", ["slug"], [], false),
|
|
1221
|
+
]),
|
|
1222
|
+
operation(772, "region.set", "Create the region of this slug (the name is required) or change its name, country or state; a field left out keeps its value. A host joins a region through host.update", [
|
|
1223
|
+
route("PUT", "/v1/_regions/{slug}", ["slug"], [], true),
|
|
1224
|
+
]),
|
|
1225
|
+
operation(668, "release.list", "List this host's platform releases, newest first: every folder of <root>/releases holding the server binary (the folder's name is the commit it was built from, with the binary's size and time), which one this process runs, which one the current link names (the next restart's) and which one the previous link names (a rollback's), and restart_pending when current is not the running one. A release is switched on the host by deploy-stax.sh, which health-checks it and rolls back", [
|
|
1226
|
+
route("GET", "/v1/_releases", [], [], false),
|
|
1227
|
+
]),
|
|
1228
|
+
operation(223, "role.assign", "Assign a role to one end user: the route's {id} and the role in the body", [
|
|
1229
|
+
route("POST", "/v1/_users/{id}/role", ["id"], [], true),
|
|
1230
|
+
route("PUT", "/v1/_users/{id}/role", ["id"], [], true),
|
|
1231
|
+
]),
|
|
1232
|
+
operation(318, "role.create", "Create a role: a name with the operations it grants and denies, optionally inheriting another role", [
|
|
1233
|
+
route("POST", "/v1/_roles", [], [], true),
|
|
1234
|
+
route("POST", "/_roles", [], [], true),
|
|
1235
|
+
]),
|
|
1236
|
+
operation(348, "role.delete", "Delete one role by its name (the route's {name}); role.list shows the roles", [
|
|
1237
|
+
route("DELETE", "/v1/_roles/{name}", ["name"], [], false),
|
|
1238
|
+
route("DELETE", "/_roles/{name}", ["name"], [], false),
|
|
1239
|
+
]),
|
|
1240
|
+
operation(222, "role.list", "List the app's roles with what each grants and denies", [
|
|
1241
|
+
route("GET", "/v1/_roles", [], [], false),
|
|
1242
|
+
route("GET", "/_roles", [], [], false),
|
|
1243
|
+
]),
|
|
1244
|
+
operation(347, "role.update", "Change one role by name; see the schema for the fields", [
|
|
1245
|
+
route("PATCH", "/v1/_roles/{name}", ["name"], [], true),
|
|
1246
|
+
route("PUT", "/v1/_roles/{name}", ["name"], [], true),
|
|
1247
|
+
route("PATCH", "/_roles/{name}", ["name"], [], true),
|
|
1248
|
+
]),
|
|
1249
|
+
operation(675, "root_domain.add", "Add a root domain (onstax.com) that apps answer under as <slug>.<root> and their test hosts as <label>.<root>: stored before the call answers, published to the front as *.<root> and, when the platform has a DNS-01 token, its wildcard certificate requested from the configured ACME provider. Answers 201 with domain, enabled, source, hosts and added_at; KEY_EXISTS (field domain) when the platform has it, INVALID_ARGUMENT (field domain) when it is not a domain", [
|
|
1250
|
+
route("POST", "/v1/_root_domains", [], [], true),
|
|
1251
|
+
]),
|
|
1252
|
+
operation(676, "root_domain.list", "List the root domains: those of substrio.conf (source config) then the ones added (source admin), each with enabled and its count of test hosts", [
|
|
1253
|
+
route("GET", "/v1/_root_domains", [], [], false),
|
|
1254
|
+
]),
|
|
1255
|
+
operation(678, "root_domain.remove", "Remove an added root domain; refused ROOT_DOMAIN_IN_USE (hosts: the count) while a test host is bound under it, UNSUPPORTED_OPERATION (field domain) for one of substrio.conf. Answers 204", [
|
|
1256
|
+
route("DELETE", "/v1/_root_domains/{domain}", ["domain"], [], false),
|
|
1257
|
+
]),
|
|
1258
|
+
operation(677, "root_domain.set", "Enable or disable an added root domain: disabled, no host answers under it and none is bound; a config root is refused UNSUPPORTED_OPERATION (field domain)", [
|
|
1259
|
+
route("PATCH", "/v1/_root_domains/{domain}", ["domain"], [], true),
|
|
1260
|
+
]),
|
|
1261
|
+
operation(405, "route.call", "Send one HTTP request to a route of the app or of one of its sandboxes and return the response", [
|
|
1262
|
+
route("POST", "/v1/_route", [], [], true),
|
|
1263
|
+
]),
|
|
1264
|
+
operation(486, "route.list", "List the HTTP routes the deployed functions answer: method, pattern, function and version; routes are declared in each function's manifest at deploy, there is no separate edit", [
|
|
1265
|
+
route("GET", "/v1/_routes", [], [], false),
|
|
1266
|
+
]),
|
|
1267
|
+
operation(402, "sandbox.allow.add", "Let a sandbox send outbound requests to hosts matching a pattern", [
|
|
1268
|
+
route("POST", "/v1/_sandboxes/{name}/allow", ["name"], [], true),
|
|
1269
|
+
]),
|
|
1270
|
+
operation(401, "sandbox.allow.list", "List the host patterns a sandbox may send outbound requests to", [
|
|
1271
|
+
route("GET", "/v1/_sandboxes/{name}/allow", ["name"], [], false),
|
|
1272
|
+
]),
|
|
1273
|
+
operation(403, "sandbox.allow.remove", "Stop letting a sandbox send outbound requests to hosts matching a pattern", [
|
|
1274
|
+
route("DELETE", "/v1/_sandboxes/{name}/allow/{pattern}", ["name","pattern"], [], false),
|
|
1275
|
+
]),
|
|
1276
|
+
operation(394, "sandbox.create", "Create a sandbox: a copy of the app's data running the app's live version", [
|
|
1277
|
+
route("POST", "/v1/_sandboxes", [], [], true),
|
|
1278
|
+
]),
|
|
1279
|
+
operation(397, "sandbox.delete", "Delete one sandbox by name together with its data; the app itself is not touched. To keep the sandbox and only copy the app's data into it again, use sandbox.reset", [
|
|
1280
|
+
route("DELETE", "/v1/_sandboxes/{name}", ["name"], [], false),
|
|
1281
|
+
]),
|
|
1282
|
+
operation(395, "sandbox.list", "List the app's sandboxes, their hosts and live versions", [
|
|
1283
|
+
route("GET", "/v1/_sandboxes", [], [], false),
|
|
1284
|
+
]),
|
|
1285
|
+
operation(400, "sandbox.outbox", "Read the outbound requests a sandbox's fence captured instead of sending", [
|
|
1286
|
+
route("GET", "/v1/_sandboxes/{name}/outbox", ["name"], [], false),
|
|
1287
|
+
]),
|
|
1288
|
+
operation(396, "sandbox.reset", "Copy the app's data into a sandbox again, keeping its live version", [
|
|
1289
|
+
route("POST", "/v1/_sandboxes/{name}/reset", ["name"], [], false),
|
|
1290
|
+
]),
|
|
1291
|
+
operation(573, "sandbox.stub.clear", "Forget a test tenant's stubs and the recorded fetches; answers how many of each", [
|
|
1292
|
+
route("DELETE", "/v1/_stubs", [], [], false),
|
|
1293
|
+
]),
|
|
1294
|
+
operation(572, "sandbox.stub.list", "Read a test tenant's stubs, with how often each answered, and every fetch its functions made since the last clear (method, URL, headers, body, the tenant's clock, stubbed or unmatched)", [
|
|
1295
|
+
route("GET", "/v1/_stubs", [], [], false),
|
|
1296
|
+
]),
|
|
1297
|
+
operation(571, "sandbox.stub.set", "Stub one outbound request of a test tenant: its functions' fetch() of the given method, origin and path resolves with the stub's status, headers and body instead of going out. Only a test tenant takes stubs, any other is refused with TEST_TENANT_REQUIRED. The newest matching stub that is not spent answers; a fetch no stub matches is rejected. Answers {id}", [
|
|
1298
|
+
route("POST", "/v1/_stubs", [], [], true),
|
|
1299
|
+
]),
|
|
1300
|
+
operation(234, "schedule.create", "Create a cron schedule: body function (the function to run, which must define a scheduled handler or a default export) and expression (five fields; one that never fires, like 31 February, is refused), optionally timezone (an IANA zone id the expression is evaluated in, default UTC; an unknown id is refused) and catch_up (none, last or all: what fires missed while the server was down do; default last). A run that fails or is refused is retried with backoff (settings cron.max_attempts, cron.backoff_ms), then goes to _dead_letters. Answers {id, success}; the id is what schedule.run, schedule.next, schedule.history, schedule.update and schedule.delete take", [
|
|
1301
|
+
route("POST", "/v1/_cron", [], [], true),
|
|
1302
|
+
]),
|
|
1303
|
+
operation(235, "schedule.delete", "Delete one cron schedule by id; 404 when it does not exist", [
|
|
1304
|
+
route("DELETE", "/v1/_cron/{id}", ["id"], [], false),
|
|
1305
|
+
]),
|
|
1306
|
+
operation(482, "schedule.history", "Page a schedule's stored runs, newest first: start time (Unix seconds and ISO 8601 UTC), duration, outcome and the function.errors entry id of a failed run; every run is kept; page with count and cursor", [
|
|
1307
|
+
route("GET", "/v1/_cron/{id}/history", ["id"], ["cursor","count"], false),
|
|
1308
|
+
]),
|
|
1309
|
+
operation(233, "schedule.list", "List the app's cron schedules; schedule.next and schedule.history say when they fire and what happened", [
|
|
1310
|
+
route("GET", "/v1/_cron", [], [], false),
|
|
1311
|
+
]),
|
|
1312
|
+
operation(481, "schedule.next", "List a schedule's next fire times (in its time zone's wall clock) as Unix seconds and ISO 8601 UTC; an expression that never fires is refused", [
|
|
1313
|
+
route("GET", "/v1/_cron/{id}/next", ["id"], ["count"], false),
|
|
1314
|
+
]),
|
|
1315
|
+
operation(480, "schedule.run", "Run a schedule's function now with its cron event, and record the run in the schedule's history", [
|
|
1316
|
+
route("POST", "/v1/_cron/{id}/run", ["id"], [], false),
|
|
1317
|
+
]),
|
|
1318
|
+
operation(287, "schedule.update", "Enable or disable one cron schedule: body enabled (required boolean), the route's {id}. Answers {success, updated, enabled}; 404 when no such schedule", [
|
|
1319
|
+
route("PATCH", "/v1/_cron/{id}", ["id"], [], true),
|
|
1320
|
+
]),
|
|
1321
|
+
operation(323, "schema.delete", "Delete a collection's schema (the route's {collection})", [
|
|
1322
|
+
route("DELETE", "/v1/{collection}/_schema", ["collection"], [], false),
|
|
1323
|
+
]),
|
|
1324
|
+
operation(52, "schema.get", "Read a collection's schema: its rules (field, type, required, min, max, pattern, enum), its constraints across fields when it has any, and whether it is strict", [
|
|
1325
|
+
route("GET", "/v1/{collection}/_schema", ["collection"], [], false),
|
|
1326
|
+
]),
|
|
1327
|
+
operation(53, "schema.update", "Set a collection's schema, replacing the old one: body rules (each rule's field is a document field name kept exactly as given, an underscore first included; _id, _createdAt, _updatedAt and _version are the system's and refused), optional constraints and strict (default true, which refuses fields no rule names; every answer states the strict in force); at least one rule or constraint. 404 COLLECTION_NOT_FOUND when the collection does not exist: create it first. A rule's type is string, number, boolean, array, object, datetime (an RFC 3339 / ISO 8601 date-time string, kept as given, or an integer epoch in milliseconds), secret (a string field no read returns: it is left out of every document, result, export and event, a filter, sort, group or projection naming it answers 400 SECRET_FIELD, and it takes no enum, constraint, unique, index, text, keyword, top or embedding; a function reads it with db.secret) or any; any other word is refused with 400 INVALID_ARGUMENT. null counts as absent: a field that is not required accepts null, a required one refuses it unless its rule sets nullable to true; min and max bound a number's value or a string's length; pattern is a regular expression for strings; enum lists the allowed strings. A constraint is {kind, ...}: exactly_one, at_least_one or at_most_one with fields (two or more), or required_when with field, equals, required and forbidden (for example {\"kind\":\"required_when\",\"field\":\"kind\",\"equals\":\"x\",\"required\":[\"a\"],\"forbidden\":[\"c\"]}); every field a constraint names needs a string, number, boolean or datetime rule in the same schema. A write that breaks a constraint answers 400 SCHEMA_REFUSED with the violation's constraint kind and fields (never a value). Stored documents that already break a constraint refuse the declaration with 409 SCHEMA_VIOLATORS ({count, first:{_id}}); existing \"allow\" declares anyway. Answers the stored schema", [
|
|
1328
|
+
route("PATCH", "/v1/{collection}/_schema", ["collection"], [], true),
|
|
1329
|
+
route("POST", "/v1/{collection}/_schema", ["collection"], [], true),
|
|
1330
|
+
]),
|
|
1331
|
+
operation(279, "search.analytics", "Read search analytics for a collection", [
|
|
1332
|
+
route("GET", "/v1/{collection}/_analytics/search", ["collection"], ["since","top_n"], false),
|
|
1333
|
+
]),
|
|
1334
|
+
operation(672, "secret.delete", "Delete one app secret by its key (the route's {key}): answers deleted and the key. The value is gone and cannot be restored; secret.write sets a new one. A key the app does not hold is SECRET_NOT_FOUND naming key", [
|
|
1335
|
+
route("DELETE", "/v1/_secrets/{key}", ["key"], [], false),
|
|
1336
|
+
]),
|
|
1337
|
+
operation(179, "secret.list", "List the names of the app's secrets: answers secrets (each {key, value:'***'}) and count. Values are never returned; set or delete one with secret.write", [
|
|
1338
|
+
route("GET", "/v1/_secrets", [], [], false),
|
|
1339
|
+
]),
|
|
1340
|
+
operation(180, "secret.write", "Set an app secret: key and value (both required, not empty). The value cannot be read back, only overwritten; secret.list shows names and secret.delete removes one", [
|
|
1341
|
+
route("PUT", "/v1/_secrets", [], [], true),
|
|
1342
|
+
route("POST", "/v1/_secrets", [], [], true),
|
|
1343
|
+
]),
|
|
1344
|
+
operation(176, "security.allowlist", "Manage network allowlist", [
|
|
1345
|
+
route("GET", "/v1/_security/allowlist", [], ["target","ip","clear"], false),
|
|
1346
|
+
route("POST", "/v1/_security/allowlist", [], [], true),
|
|
1347
|
+
route("DELETE", "/v1/_security/allowlist", [], [], true),
|
|
1348
|
+
]),
|
|
1349
|
+
operation(178, "security.audit", "Read the failed sign-ins of the audit trail (action auth.failure in audit.list), newest first: the key id of the key that proved nothing, or the caller's address when no key was presented; since, until, order, limit, cursor, actor_kind and actor filter it", [
|
|
1350
|
+
route("GET", "/v1/_security/audit", [], ["order","since","limit","cursor","until","actor_kind","event_type"], false),
|
|
1351
|
+
]),
|
|
1352
|
+
operation(177, "security.rate_limit", "Manage the per-address rate limit (the --rate-limit server option): GET shows it, POST sets max_tokens and tokens_per_second, DELETE with clear turns it off", [
|
|
1353
|
+
route("GET", "/v1/_security/rate_limit", [], ["max_tokens","tokens_per_second","clear"], false),
|
|
1354
|
+
route("POST", "/v1/_security/rate_limit", [], [], true),
|
|
1355
|
+
route("DELETE", "/v1/_security/rate_limit", [], [], true),
|
|
1356
|
+
]),
|
|
1357
|
+
operation(259, "security.rules.delete", "Delete the app's collection security rules; answers {ok}", [
|
|
1358
|
+
route("DELETE", "/v1/_rules", [], [], false),
|
|
1359
|
+
]),
|
|
1360
|
+
operation(257, "security.rules.get", "Read the app's collection security rules: which callers may read, create, write and delete in each collection", [
|
|
1361
|
+
route("GET", "/v1/_rules", [], [], false),
|
|
1362
|
+
]),
|
|
1363
|
+
operation(258, "security.rules.set", "Replace the app's collection security rules: body rules, a list of {collection (or * for every collection without a rule of its own), read, write, create, delete}, each access one of allow, auth, owner, deny or role:<name>. Answers the stored rules. A version can carry rules too (version.create)", [
|
|
1364
|
+
route("POST", "/v1/_rules", [], [], true),
|
|
1365
|
+
]),
|
|
1366
|
+
operation(282, "settings.delete", "Delete one app setting by key; a setting substrio reads goes back to its default", [
|
|
1367
|
+
route("DELETE", "/v1/_settings/{key}", ["key"], [], false),
|
|
1368
|
+
]),
|
|
1369
|
+
operation(240, "settings.get", "List the app's settings: answers settings (each {key, value}) and count", [
|
|
1370
|
+
route("GET", "/v1/_settings", [], [], false),
|
|
1371
|
+
]),
|
|
1372
|
+
operation(241, "settings.set", "Set one app setting: body key and value (a string, or true or false). Any key is stored; substrio reads changes.history (keep change history so change streams can resume) and webhooks.brand (the webhook signature and delivery id header names, <brand>-Signature and <brand>-Delivery-Id) and the delivery settings webhooks.max_attempts, webhooks.backoff_ms, triggers.max_attempts, triggers.backoff_ms, cron.max_attempts, cron.backoff_ms, delivery.backoff_cap_ms (milliseconds) and delivery.retain_days (days a delivered record stays replayable, 0 keeps none), each a whole number (the attempt counts at least 1), and refuses a value that does not suit them. A server-wide runtime limit (jsc.*, queue.*, objects.*) is a platform setting and is refused here with SETTING_IS_PLATFORM", [
|
|
1373
|
+
route("PUT", "/v1/_settings", [], [], true),
|
|
1374
|
+
route("POST", "/v1/_settings", [], [], true),
|
|
1375
|
+
]),
|
|
1376
|
+
operation(362, "site.blob.upload", "Upload one site or function file a version or release names. With no hash the server names it by the SHA-256 it takes and answers hash and size; a given hash is checked against the bytes. A file over one MCP call goes in parts (offset, total): with no hash the first part answers an upload id that every later part carries. The answered hash is the unit's id in version.commit (units: [{path, id}]), so a large file is committed with no bytes sent twice; a small file is committed as text or base64 instead", [
|
|
1377
|
+
route("PUT", "/v1/_site/blobs/{hash}", ["hash"], [], false),
|
|
1378
|
+
route("PUT", "/v1/_blobs/{hash}", ["hash"], [], false),
|
|
1379
|
+
route("PUT", "/v1/_site/blobs", [], [], false),
|
|
1380
|
+
]),
|
|
1381
|
+
operation(363, "site.release.activate", "Serve a site release, or roll back to an earlier one", [
|
|
1382
|
+
route("POST", "/v1/_site/releases/{id}/activate", ["id"], [], false),
|
|
1383
|
+
]),
|
|
1384
|
+
operation(361, "site.release.create", "Record a site release from a manifest of file hashes and list the blobs to upload", [
|
|
1385
|
+
route("POST", "/v1/_site/releases", [], [], true),
|
|
1386
|
+
]),
|
|
1387
|
+
operation(364, "site.release.list", "List the static site releases and which one is active; site.release.activate serves one or rolls back to an earlier one", [
|
|
1388
|
+
route("GET", "/v1/_site/releases", [], [], false),
|
|
1389
|
+
]),
|
|
1390
|
+
operation(674, "source.edit", "Edit one unit of a branch by exact match and commit it: each edit replaces old (which must occur exactly once, unless replace_all) with new, in order, and all of them land as one commit or none does. EDIT_NO_MATCH names the closest line, EDIT_AMBIGUOUS the count and the lines; both carry the path as field. A text unit only (a binary one is written whole with version.commit). Answers what version.commit answers, with replaced (the occurrences replaced)", [
|
|
1391
|
+
route("POST", "/v1/_source/edit", [], [], true),
|
|
1392
|
+
]),
|
|
1393
|
+
operation(701, "source.move", "Move one unit of a branch to a new path as one commit: the old path removed and the same object written at the new one (no byte copied). The new path must be free. Answers what version.commit answers", [
|
|
1394
|
+
route("POST", "/v1/_source/move", [], [], true),
|
|
1395
|
+
]),
|
|
1396
|
+
operation(700, "source.search", "Search the text units of a version (the route's {version}) for pattern: exact bytes, or a regular expression with regex:true (linear time, line by line). glob narrows the files; context adds lines before and after. Answers {version, matches: [{path, line, text, before, after}], count, truncated}, bounded by max_matches and max_bytes", [
|
|
1397
|
+
route("GET", "/v1/_commits/{version}/search", ["version"], ["max_bytes","max_matches","branch","glob","pattern","regex","context"], false),
|
|
1398
|
+
]),
|
|
1399
|
+
operation(778, "source.shell", "Run one shell command over the app, the way you work on a checkout: no process, a POSIX shell subset interpreted in the server. Mounts: /src is the branch's tree (writable, default main, the starting directory), /site the live release's site/ (read only), /files the app's stored files (writable; rm there is refused, file.delete does it). Any other path is refused. Builtins: grep (-r -n -l -L -c -i -v -w -o -q -E -F -A -B -C -m --include --exclude), sed (-n -E -i -e, addresses N $ /re/ ranges !, s///g with \\1 groups, p d q a i c y { }), find (-name -iname -path -type -maxdepth -mindepth -empty -exec {} + -delete), cat, head, tail, wc, sort, uniq, cut, tr, diff (unified), ls, cd, pwd, mv, cp, rm, mkdir, touch, echo, printf, tee, test and [ ], xargs, basename, dirname, true, false, commit -m, and js (a script in the app's own runtime with fs.readFile, writeFile, readdir, stat, exists, rm, rename, mkdir on the same mounts; input is stdin). Variables: $PWD $HOME $SRC $SITE $FILES $BRANCH $?. Every /src change of one call lands as ONE commit (message: the command, or commit -m) when the command ends with status 0; any other status, or any refusal, lands nothing. No state is kept between calls. Answers {stdout, stderr, exit_code, offset, truncated, next_offset, commit: {version, number, changed}}; output past the budget is cut with truncated:true and continued with offset. Examples: grep -rn 'TODO' /src; sed -n 10,40p src/app.js; find /src/functions -name '*.js' | xargs sed -i 's/oldName/newName/g'; cat > /src/site/about.html <<'EOF' (lines) EOF; js 'fs.writeFile(\"notes.txt\", input.toUpperCase())' < notes.txt", [
|
|
1400
|
+
route("POST", "/v1/_source/shell", [], [], true),
|
|
1401
|
+
]),
|
|
1402
|
+
operation(673, "source.tree", "List the paths a version holds (the route's {version}: an id, a number, a branch, live or a sandbox name), ordered by path, each with its kind (file or dir). prefix walks only that directory; glob keeps the files whose path matches (* and ? also match /); sizes:true adds each file's size. Answers {version, entries, count}", [
|
|
1403
|
+
route("GET", "/v1/_commits/{version}/tree", ["version"], ["branch","prefix","glob","sizes"], false),
|
|
1404
|
+
]),
|
|
1405
|
+
operation(475, "sql.batch", "Run SQL statements with bound parameters in order in one transaction; a failure rolls every row write back and names the failing statement's index, errno, sqlstate and message", [
|
|
1406
|
+
route("POST", "/v1/_sql/batch", [], [], true),
|
|
1407
|
+
]),
|
|
1408
|
+
operation(474, "sql.schema", "Describe the SQL catalog from the engine's typed descriptors: tables and views with columns (name, declared type, nullable, default, key, generated), indexes (columns with order, unique), foreign keys, and routines; pass table for one table", [
|
|
1409
|
+
route("GET", "/v1/_sql/schema", [], ["table"], false),
|
|
1410
|
+
]),
|
|
1411
|
+
operation(755, "subscriptions.cancel", "End one of the signed-in person's own subscriptions now: a paid one leaves the account's provider subscription that pays it (that line, or the whole subscription when it was the last) and the person falls back to what else they hold there (a bundle's plan, else the app's default). A bundle child ends only with its Substrio plan (SUBSCRIPTION_NOT_FOUND). The person's own session only, never a key or an agent (SUBSCRIPTION_PERSON_REQUIRED). Answers the subscription, ended", [
|
|
1412
|
+
route("POST", "/v1/_subscriptions/{id}/cancel", ["id"], [], false),
|
|
1413
|
+
]),
|
|
1414
|
+
operation(754, "subscriptions.list", "List an app's subscribers a page at a time: every subscription to its plans (a purchase, a bundle child of a Substrio plan, or a grant), by subject (an account, a person, or an app's own user) and id. The caller's own app, or (app) another app of the same owning account; any other app's is refused SUBSCRIBERS_NOT_VISIBLE", [
|
|
1415
|
+
route("GET", "/v1/_subscriptions", [], ["app","cursor","limit"], false),
|
|
1416
|
+
]),
|
|
1417
|
+
operation(753, "subscriptions.mine", "List the signed-in person's own subscriptions at every app, active and ended: what they bought for themselves (a cart for the person), each Substrio plan's bundle children, and the plans apps granted them. A person's session only (at the console or at any app); a key is refused SUBSCRIPTION_PERSON_REQUIRED", [
|
|
1418
|
+
route("GET", "/v1/_subscriptions/mine", [], [], false),
|
|
1419
|
+
]),
|
|
1420
|
+
operation(211, "sync.push", "Push replication operations", [
|
|
1421
|
+
route("POST", "/v1/_sync", [], [], true),
|
|
1422
|
+
]),
|
|
1423
|
+
operation(212, "sync.relay", "Relay replication traffic", [
|
|
1424
|
+
route("GET", "/v1/_sync/relay", [], [], false),
|
|
1425
|
+
]),
|
|
1426
|
+
operation(210, "sync.status", "Read replication state", [
|
|
1427
|
+
route("GET", "/v1/_sync", [], [], false),
|
|
1428
|
+
]),
|
|
1429
|
+
operation(249, "system.async_ping", "Check liveness through the worker path", [
|
|
1430
|
+
route("GET", "/sping_async", [], [], false),
|
|
1431
|
+
route("GET", "/v1/sping_async", [], [], false),
|
|
1432
|
+
]),
|
|
1433
|
+
operation(2, "system.health", "Read health state", [
|
|
1434
|
+
route("GET", "/health", [], [], false),
|
|
1435
|
+
]),
|
|
1436
|
+
operation(1, "system.ping", "Check process liveness", [
|
|
1437
|
+
route("GET", "/ping", [], [], false),
|
|
1438
|
+
]),
|
|
1439
|
+
operation(304, "system.reset", "Drop the app's whole database and start a fresh empty one: every collection, SQL table, row, index and the change history are destroyed and this cannot be undone. Functions, versions, secrets and settings are kept. Answers {status: reset, file_size_bytes}; take a backup first", [
|
|
1440
|
+
route("POST", "/v1/_reset", [], [], false),
|
|
1441
|
+
]),
|
|
1442
|
+
operation(623, "system.telemetry", "Read this host's telemetry for the platform operator: version, uptime since the process started, OS, CPU, memory, disk, load and the server process's own use", [
|
|
1443
|
+
route("GET", "/v1/_system/telemetry", [], [], false),
|
|
1444
|
+
]),
|
|
1445
|
+
operation(628, "tag.create", "Mark a version with a name that never moves again: name it and say which version (by id or number), or name a branch whose head it marks (default main). 409 when the name is taken. Answers {name, version}", [
|
|
1446
|
+
route("POST", "/v1/_tags", [], [], true),
|
|
1447
|
+
]),
|
|
1448
|
+
operation(629, "tag.list", "List the app's tags by name, each with the version it marks and when it was made; never refuses", [
|
|
1449
|
+
route("GET", "/v1/_tags", [], [], false),
|
|
1450
|
+
]),
|
|
1451
|
+
operation(574, "tenant.clock.set", "Set a test tenant's clock: Date.now() and new Date() in its functions read it. real, frozen at an instant, or real time plus an offset; setTimeout and cron stay on real time. Only a test tenant has a clock, any other is refused with TEST_TENANT_REQUIRED. Answers the setting and the clock's reading", [
|
|
1452
|
+
route("POST", "/v1/_clock", [], [], true),
|
|
1453
|
+
]),
|
|
1454
|
+
operation(579, "tenant.clone", "Create a new tenant as a copy of an existing one; the source stays online", [
|
|
1455
|
+
route("POST", "/v1/_tenants/{slug}/clone", ["slug"], [], true),
|
|
1456
|
+
route("POST", "/v1/tenants/{slug}/clone", ["slug"], [], true),
|
|
1457
|
+
route("POST", "/v1/projects/{slug}/clone", ["slug"], [], true),
|
|
1458
|
+
]),
|
|
1459
|
+
operation(33, "tenant.create", "Create an isolated tenant", [
|
|
1460
|
+
route("POST", "/v1/_tenants", [], [], true),
|
|
1461
|
+
route("POST", "/v1/tenants", [], [], true),
|
|
1462
|
+
route("POST", "/v1/projects", [], [], true),
|
|
1463
|
+
]),
|
|
1464
|
+
operation(36, "tenant.delete", "Delete a tenant and its database", [
|
|
1465
|
+
route("DELETE", "/v1/_tenants/{slug}", ["slug"], [], false),
|
|
1466
|
+
route("DELETE", "/v1/tenants/{slug}", ["slug"], [], false),
|
|
1467
|
+
route("DELETE", "/v1/projects/{slug}", ["slug"], [], false),
|
|
1468
|
+
]),
|
|
1469
|
+
operation(40, "tenant.domain.attach", "Attach a domain to the app: it routes at once; a domain another app holds is refused", [
|
|
1470
|
+
route("POST", "/v1/_tenant/domains", [], [], true),
|
|
1471
|
+
]),
|
|
1472
|
+
operation(41, "tenant.domain.detach", "Detach one custom domain from this app by hostname", [
|
|
1473
|
+
route("DELETE", "/v1/_tenant/domains/{domain}", ["domain"], [], false),
|
|
1474
|
+
]),
|
|
1475
|
+
operation(39, "tenant.domain.list", "List the custom domains attached to this app", [
|
|
1476
|
+
route("GET", "/v1/_tenant/domains", [], [], false),
|
|
1477
|
+
]),
|
|
1478
|
+
operation(398, "tenant.fetch_allow.get", "List the private host:port addresses a tenant's outbound requests may reach", [
|
|
1479
|
+
route("GET", "/v1/_tenants/{slug}/fetch-allow", ["slug"], [], false),
|
|
1480
|
+
]),
|
|
1481
|
+
operation(399, "tenant.fetch_allow.set", "Replace the private host:port addresses a tenant's outbound requests may reach", [
|
|
1482
|
+
route("PUT", "/v1/_tenants/{slug}/fetch-allow", ["slug"], [], true),
|
|
1483
|
+
]),
|
|
1484
|
+
operation(34, "tenant.get", "Read tenant configuration", [
|
|
1485
|
+
route("GET", "/v1/_tenants/{slug}", ["slug"], [], false),
|
|
1486
|
+
route("GET", "/v1/tenants/{slug}", ["slug"], [], false),
|
|
1487
|
+
route("GET", "/v1/projects/{slug}", ["slug"], [], false),
|
|
1488
|
+
]),
|
|
1489
|
+
operation(43, "tenant.key.list", "List the tenant's keys: ids, when each was made, when it expires, and whether it is current, previous (still in its overlap) or expired; never a key", [
|
|
1490
|
+
route("GET", "/v1/_tenant/keys", [], [], false),
|
|
1491
|
+
]),
|
|
1492
|
+
operation(44, "tenant.key.revoke", "End the previous key's overlap now, by key id; the current key is replaced with tenant.key.rotate", [
|
|
1493
|
+
route("DELETE", "/v1/_tenant/keys/{id}", ["id"], [], false),
|
|
1494
|
+
]),
|
|
1495
|
+
operation(38, "tenant.key.rotate", "Replace the tenant's key and return the new one once; the old key stops now, or after overlap_seconds so a deploy can switch over", [
|
|
1496
|
+
route("POST", "/v1/_tenant/key", [], [], true),
|
|
1497
|
+
]),
|
|
1498
|
+
operation(45, "tenant.key.update", "Set or clear a key's own requests_per_minute and request_budget, by key id; a field left out keeps its current value. Rotating or revoking the key is unaffected", [
|
|
1499
|
+
route("PATCH", "/v1/_tenant/keys/{id}", ["id"], [], true),
|
|
1500
|
+
]),
|
|
1501
|
+
operation(613, "tenant.limits", "Read a tenant's quota, document and request limits and their live usage", [
|
|
1502
|
+
route("GET", "/v1/_tenants/{slug}/limits", ["slug"], [], false),
|
|
1503
|
+
route("GET", "/v1/_limits", [], [], false),
|
|
1504
|
+
]),
|
|
1505
|
+
operation(612, "tenant.limits.set", "Set a tenant's quota, document and request limits (storage bytes, documents, requests per minute, max document size, max request bytes, cf fetch calls, cf fetch response megabytes, realtime connections, functions, cron triggers, collections); an unknown field refuses the call", [
|
|
1506
|
+
route("PATCH", "/v1/_tenants/{slug}/limits", ["slug"], [], true),
|
|
1507
|
+
]),
|
|
1508
|
+
operation(32, "tenant.list", "List tenants visible to the caller", [
|
|
1509
|
+
route("GET", "/v1/_tenants", [], ["limit","cursor"], false),
|
|
1510
|
+
route("GET", "/v1/tenants", [], ["limit","cursor"], false),
|
|
1511
|
+
route("GET", "/v1/projects", [], ["limit","cursor"], false),
|
|
1512
|
+
]),
|
|
1513
|
+
operation(665, "tenant.place", "Place an app on a host (host 0 takes it off its host) in one commit: the old host's count of apps down, the new host's up. Refused with HOST_AT_CAPACITY when the host holds its max_tenants, HOST_NOT_FOUND when there is no such host. Placing an app on the host it is on changes nothing. Answers slug and host", [
|
|
1514
|
+
route("POST", "/v1/_tenants/{slug}/place", ["slug"], [], true),
|
|
1515
|
+
]),
|
|
1516
|
+
operation(578, "tenant.rename", "Rename a tenant's slug; nothing on disk moves and no issued key is invalidated", [
|
|
1517
|
+
route("POST", "/v1/_tenants/{slug}/rename", ["slug"], [], true),
|
|
1518
|
+
route("POST", "/v1/tenants/{slug}/rename", ["slug"], [], true),
|
|
1519
|
+
route("POST", "/v1/projects/{slug}/rename", ["slug"], [], true),
|
|
1520
|
+
]),
|
|
1521
|
+
operation(577, "tenant.resume", "Resume a suspended tenant, restoring the state before it was suspended", [
|
|
1522
|
+
route("POST", "/v1/_tenants/{slug}/resume", ["slug"], [], false),
|
|
1523
|
+
route("POST", "/v1/tenants/{slug}/resume", ["slug"], [], false),
|
|
1524
|
+
route("POST", "/v1/projects/{slug}/resume", ["slug"], [], false),
|
|
1525
|
+
]),
|
|
1526
|
+
operation(788, "tenant.storage", "Read what this app's folder holds on disk (disk_bytes, as of the last fold, each second) by kind (database, files, logs, backups, code, other), its storage limit and the room left (null while no limit is set). The storage limit is held against disk_bytes; tenant.usage's used_bytes and blob_bytes count only document and file bytes", [
|
|
1527
|
+
route("GET", "/v1/_storage", [], [], false),
|
|
1528
|
+
]),
|
|
1529
|
+
operation(576, "tenant.suspend", "Suspend a tenant with a typed reason and an optional note", [
|
|
1530
|
+
route("POST", "/v1/_tenants/{slug}/suspend", ["slug"], [], true),
|
|
1531
|
+
route("POST", "/v1/tenants/{slug}/suspend", ["slug"], [], true),
|
|
1532
|
+
route("POST", "/v1/projects/{slug}/suspend", ["slug"], [], true),
|
|
1533
|
+
]),
|
|
1534
|
+
operation(35, "tenant.update", "Update tenant configuration", [
|
|
1535
|
+
route("PATCH", "/v1/_tenants/{slug}", ["slug"], [], true),
|
|
1536
|
+
route("PATCH", "/v1/tenants/{slug}", ["slug"], [], true),
|
|
1537
|
+
route("PATCH", "/v1/projects/{slug}", ["slug"], [], true),
|
|
1538
|
+
]),
|
|
1539
|
+
operation(37, "tenant.usage", "Read a tenant's exact usage (stored documents and their bytes) and its limits; a limit is null until one is set", [
|
|
1540
|
+
route("GET", "/v1/_tenants/{slug}/usage", ["slug"], [], false),
|
|
1541
|
+
]),
|
|
1542
|
+
operation(619, "tenant.usage.history", "Read this app's KPI history, oldest first: one point per finished minute (step=minute, the default; kept two days) or hour (step=hour; kept 90 days), each with what the app's function calls added in it (calls, status classes, latency buckets and percentiles, refusals, CPU and wall time) and its stored documents and bytes at its end. since and until are Unix seconds (default: the last hour of minutes, or the last two days of hours); limit caps the points. Counted on the node that answers", [
|
|
1543
|
+
route("GET", "/v1/_usage/history", [], ["limit","since","until","step"], false),
|
|
1544
|
+
]),
|
|
1545
|
+
operation(484, "tenant.usage.self", "Read this tenant's own exact usage and limits: storage bytes, documents, requests per minute (a limit is null until one is set) and the function limits (cpu, memory, concurrency, timeout, body and value sizes), for the tenant the key belongs to", [
|
|
1546
|
+
route("GET", "/v1/_usage", [], [], false),
|
|
1547
|
+
]),
|
|
1548
|
+
operation(309, "thread.ask", "Ask one question in an agent thread by id; see the schema", [
|
|
1549
|
+
route("POST", "/v1/_threads/{id}/_ask", ["id"], [], true),
|
|
1550
|
+
]),
|
|
1551
|
+
operation(161, "thread.create", "Create an agent thread, a conversation record; see the schema", [
|
|
1552
|
+
route("POST", "/v1/_threads", [], [], true),
|
|
1553
|
+
]),
|
|
1554
|
+
operation(307, "thread.delete", "Delete one agent thread by its id (the route's {id}, as thread.list shows it)", [
|
|
1555
|
+
route("DELETE", "/v1/_threads/{id}", ["id"], [], false),
|
|
1556
|
+
]),
|
|
1557
|
+
operation(305, "thread.get", "Read one agent thread by its id (the route's {id}, as thread.list shows it)", [
|
|
1558
|
+
route("GET", "/v1/_threads/{id}", ["id"], [], false),
|
|
1559
|
+
]),
|
|
1560
|
+
operation(160, "thread.list", "List the app's agent threads (conversation records); thread.get reads one by its id", [
|
|
1561
|
+
route("GET", "/v1/_threads", [], [], false),
|
|
1562
|
+
]),
|
|
1563
|
+
operation(162, "thread.message", "Append one message to an agent thread by id", [
|
|
1564
|
+
route("POST", "/v1/_threads/{id}/messages", ["id"], [], true),
|
|
1565
|
+
]),
|
|
1566
|
+
operation(321, "thread.message.delete", "Delete one message of an agent thread: the route's thread {id} and {message_id}", [
|
|
1567
|
+
route("DELETE", "/v1/_threads/{id}/messages/{message_id}", ["id","message_id"], [], false),
|
|
1568
|
+
]),
|
|
1569
|
+
operation(308, "thread.messages.list", "List the messages of one agent thread by id: options limit and order (asc or desc, default desc)", [
|
|
1570
|
+
route("GET", "/v1/_threads/{id}/messages", ["id"], ["limit","order"], false),
|
|
1571
|
+
]),
|
|
1572
|
+
operation(322, "thread.prune", "Prune selected messages from an agent thread", [
|
|
1573
|
+
route("POST", "/v1/_threads/{id}/_prune", ["id"], [], true),
|
|
1574
|
+
]),
|
|
1575
|
+
operation(163, "thread.summarize", "Summarize one agent thread by its id (the route's {id}); see the schema for the options", [
|
|
1576
|
+
route("POST", "/v1/_threads/{id}/_summarize", ["id"], [], true),
|
|
1577
|
+
]),
|
|
1578
|
+
operation(306, "thread.update", "Change one agent thread by id; see the schema", [
|
|
1579
|
+
route("PATCH", "/v1/_threads/{id}", ["id"], [], true),
|
|
1580
|
+
]),
|
|
1581
|
+
operation(580, "tool.install", "Install a tenant's command line tool from its image, named by the SHA-256 of its bytes", [
|
|
1582
|
+
route("PUT", "/v1/tools/{name}/{sha256}", ["name","sha256"], [], false),
|
|
1583
|
+
]),
|
|
1584
|
+
operation(581, "tool.list", "List a tenant's installed tools and which version of each is current", [
|
|
1585
|
+
route("GET", "/v1/tools", [], [], false),
|
|
1586
|
+
]),
|
|
1587
|
+
operation(582, "tool.remove", "Remove one installed version of a tenant's tool", [
|
|
1588
|
+
route("DELETE", "/v1/tools/{name}/{version}", ["name","version"], [], false),
|
|
1589
|
+
]),
|
|
1590
|
+
operation(583, "tool.run", "Run an installed version of a tenant's tool inside the jail, under the tenant's limits", [
|
|
1591
|
+
route("POST", "/v1/tools/{name}/run", ["name"], [], true),
|
|
1592
|
+
]),
|
|
1593
|
+
operation(261, "trigger.create", "Create a trigger: body collection, type (insert, update or delete) and function (the function to run). Answers 201 {id, success, created}; 409 for a duplicate or an invalid trigger", [
|
|
1594
|
+
route("POST", "/v1/_triggers", [], [], true),
|
|
1595
|
+
]),
|
|
1596
|
+
operation(262, "trigger.delete", "Delete one trigger by id; 404 when it does not exist", [
|
|
1597
|
+
route("DELETE", "/v1/_triggers/{id}", ["id"], [], false),
|
|
1598
|
+
]),
|
|
1599
|
+
operation(260, "trigger.list", "List the collection triggers: each runs a function after an insert, update or delete in a collection", [
|
|
1600
|
+
route("GET", "/v1/_triggers", [], [], false),
|
|
1601
|
+
]),
|
|
1602
|
+
operation(263, "trigger.update", "Enable or disable one trigger: body id and enabled; the route's {id}", [
|
|
1603
|
+
route("PATCH", "/v1/_triggers/{id}", ["id"], [], true),
|
|
1604
|
+
]),
|
|
1605
|
+
operation(437, "undo", "Undo a change by the undo token it returned; returns a token that redoes it", [
|
|
1606
|
+
route("POST", "/v1/_undo", [], [], true),
|
|
1607
|
+
]),
|
|
1608
|
+
operation(310, "user.count", "Count the app's end users; user.list pages through them", [
|
|
1609
|
+
route("GET", "/v1/_users/_count", [], ["limit","skip","cursor","prefix"], false),
|
|
1610
|
+
]),
|
|
1611
|
+
operation(218, "user.create", "Create an end user: body email, and password (or password_hash from another system) for one who signs in; with neither, a person with no credential (a contact, a guest) who is still a full user. Optional name and fields (the person object's own members), role, keyring, verified flag and metadata. Answers 201 {user_id}", [
|
|
1612
|
+
route("POST", "/v1/_users", [], [], true),
|
|
1613
|
+
]),
|
|
1614
|
+
operation(220, "user.delete", "Mark one end user deleted by id; the hard option erases the user instead", [
|
|
1615
|
+
route("DELETE", "/v1/_users/{id}", ["id"], ["hard","dry_run"], false),
|
|
1616
|
+
]),
|
|
1617
|
+
operation(664, "user.email.change", "Change one end user's address (the route's {id}): body email. The sign-in and the person object (_users) move together, the new address unverified and every session of the person ended; the old address is free at once. 409 when another person (a contact too) holds it. Answers the user as user.get does", [
|
|
1618
|
+
route("POST", "/v1/_users/{id}/email", ["id"], [], true),
|
|
1619
|
+
]),
|
|
1620
|
+
operation(217, "user.get", "Read one end user by id (from user.list)", [
|
|
1621
|
+
route("GET", "/v1/_users/{id}", ["id"], [], false),
|
|
1622
|
+
]),
|
|
1623
|
+
operation(221, "user.impersonate", "Impersonate one end user by id, to see the app as they do; admin only", [
|
|
1624
|
+
route("POST", "/v1/_users/{id}/impersonate", ["id"], [], false),
|
|
1625
|
+
]),
|
|
1626
|
+
operation(603, "user.impersonate.exit", "End an impersonated session: answers a fresh ordinary token for the administrator who started it, their own full grants restored, no impersonation mark. Only an impersonated session may call this", [
|
|
1627
|
+
route("POST", "/v1/_users/impersonate/exit", [], [], false),
|
|
1628
|
+
]),
|
|
1629
|
+
operation(216, "user.list", "List the app's end users in email order, a page at a time: limit, then cursor (the previous page's next_cursor) or skip; prefix keeps the emails that start with it. total is every person of the app", [
|
|
1630
|
+
route("GET", "/v1/_users", [], ["limit","skip","cursor","prefix"], false),
|
|
1631
|
+
]),
|
|
1632
|
+
operation(588, "user.memberships.get", "Read a user's membership: body user_id alone answers every organization held ({organization, names} rows, a range walk of the user's own key prefix); with collection and id, the one organization's held names", [
|
|
1633
|
+
route("GET", "/v1/_memberships", [], ["user_id","collection","id"], false),
|
|
1634
|
+
]),
|
|
1635
|
+
operation(589, "user.memberships.set", "Grant or revoke a user's membership in one organization (body user_id, collection, id) by permission name: body grant and revoke, either omitted, or body set alone, the exact names the membership holds after (an empty set ends it). The organization is the id as given, or derived through the collection's declared id.derive prefix. No session is ended; the gate reads the pair fresh on every request, so a revoke holds from the next one. Answers the names held in that organization", [
|
|
1636
|
+
route("POST", "/v1/_memberships", [], [], true),
|
|
1637
|
+
]),
|
|
1638
|
+
operation(311, "user.permission.check", "Ask whether one end user (the route's {id}) may run an operation on a collection: options collection and operation, both required", [
|
|
1639
|
+
route("GET", "/v1/_users/{id}/can", ["id"], ["collection","operation"], false),
|
|
1640
|
+
]),
|
|
1641
|
+
operation(586, "user.permissions.get", "Read one end user's (the route's {id}) declared permission catalog names: granted (its stored raw bits) and effective (granted, closed)", [
|
|
1642
|
+
route("GET", "/v1/_users/{id}/permissions", ["id"], [], false),
|
|
1643
|
+
]),
|
|
1644
|
+
operation(587, "user.permissions.set", "Grant or revoke one end user's (the route's {id}) declared permissions by name: body grant and revoke, either omitted. A revoke that actually removes a held bit ends the user's sessions; a grant takes effect at the next sign in. Answers the user's granted and effective names", [
|
|
1645
|
+
route("POST", "/v1/_users/{id}/permissions", ["id"], [], true),
|
|
1646
|
+
]),
|
|
1647
|
+
operation(219, "user.update", "Change one end user by id; see the schema for the fields", [
|
|
1648
|
+
route("PATCH", "/v1/_users/{id}", ["id"], [], true),
|
|
1649
|
+
]),
|
|
1650
|
+
operation(635, "version.changes", "What one commit changed (the route's {version}): every path that differs from the version it is compared against, added, removed or changed, with the object ids on both sides. against names another version (default: the commit's first parent, so the answer is the commit's own change)", [
|
|
1651
|
+
route("GET", "/v1/_commits/{version}/changes", ["version"], ["branch","against"], false),
|
|
1652
|
+
]),
|
|
1653
|
+
operation(489, "version.check", "Check an app version before it is pushed or made live: the body is the version.create body, or {version} naming one already committed (a number, an id, a branch, live or a sandbox's name; no manifest). Runs function.check over every function whose source is uploaded (parsed as script, CommonJS or ES module, with deploy's require() refusal) and answers ok, diagnostics (function, severity, message, line, column) and the blobs still missing. Nothing is stored or run. version.create and version.live.set run this same check unless they are sent check:false", [
|
|
1654
|
+
route("POST", "/v1/_versions/_check", [], [], true),
|
|
1655
|
+
]),
|
|
1656
|
+
operation(632, "version.commit", "Write one commit on a branch of the app's history: the units it changes (bytes as text, as base64, or the id of an object the app already holds, such as the hash site.blob.upload answered), the paths it removes, and what it did. This is how a file is written or deleted: one file is {message, units: [{path: \"site/index.html\", text: \"<h1>Hi</h1>\"}]}, one removal is {message, removals: [\"site/old.css\"]}; no hash is computed by the caller. A function is functions/<name>.js with its routes in functions/<name>.json ({\"routes\": [\"GET /hello\"]}), a site file site/<path>. The answered number is promotable at once: version.live.set {version: number}, with sandbox to run it there first. mode changes applies them to the parent version, mode snapshot makes the units the whole content. expected is the version the branch must still point at, so a racing commit is refused rather than overwritten. Answers {version, root, number, generation, branch, blobs_written, trees_written}", [
|
|
1657
|
+
route("POST", "/v1/_commits", [], [], true),
|
|
1658
|
+
]),
|
|
1659
|
+
operation(389, "version.create", "Push an app version: the functions (each by the SHA-256 of its source), rules, MCP manifest, site files, cron, triggers, plans, auth and login settings, and the permission and ledger catalogs (declared first when it goes live, append only). Answers 201 {version, missing, existed}: missing lists the blobs you still have to upload with site.blob.upload, and existed is true when the same content was pushed before. Pushing does not change what is live: make it live with version.live.set. The version is committed on the branch named by branch (main when none is), which is created at this version when it has no version yet. Sources already uploaded are checked like function.check unless check is false; a failing one refuses the push with 400 and its diagnostics", [
|
|
1660
|
+
route("POST", "/v1/_versions", [], [], true),
|
|
1661
|
+
]),
|
|
1662
|
+
operation(392, "version.diff", "Show what changes between two app versions (the route's {from} and {to}): the parts added, removed and changed; 404 when either does not exist", [
|
|
1663
|
+
route("GET", "/v1/_versions/{from}/diff/{to}", ["from","to"], [], false),
|
|
1664
|
+
]),
|
|
1665
|
+
operation(642, "version.gc", "Collect the objects of the app's history no name reaches any more: what a branch, a tag, an environment, a ref log inside its window and a merge in hand all keep is kept, with every ancestor, tree, unit and patch predecessor under them, and the rest is erased. dry_run works it out and erases nothing. A collection bounded by --gc-sweep stops and is carried on by the next call. Answers {started_at, finished_at, roots, marked, missing, read, collected, bytes, kept_new, foreign, dry_run, complete, resume_after}", [
|
|
1666
|
+
route("POST", "/v1/_gc", [], [], true),
|
|
1667
|
+
]),
|
|
1668
|
+
operation(643, "version.gc.status", "Where the app's collection got to: the record of the collection in hand, or of the last one that finished, with the same counts version.gc answers. started_at is 0 when the app was never collected", [
|
|
1669
|
+
route("GET", "/v1/_gc", [], [], false),
|
|
1670
|
+
]),
|
|
1671
|
+
operation(391, "version.get", "Read one app version by number (the route's {number}): its functions, rules, MCP manifest, site, cron, triggers and message, and in missing the blobs it still lacks. 404 when no such version", [
|
|
1672
|
+
route("GET", "/v1/_versions/{number}", ["number"], [], false),
|
|
1673
|
+
]),
|
|
1674
|
+
operation(390, "version.list", "List the app's versions, newest first, with the live version number (live), the version a rollback would restore (rollback_to), the live history with the backup each promote took, and each sandbox's live version. direct_changes lists, for the app and each sandbox, any function deployed directly (outside a version) since its live version was recorded. limit pages the versions and next_cursor (null on the last page) is the cursor the next page passes", [
|
|
1675
|
+
route("GET", "/v1/_versions", [], ["limit","cursor"], false),
|
|
1676
|
+
]),
|
|
1677
|
+
operation(393, "version.live.set", "Make an app version live, or roll back with version: previous; from instead of version promotes what a sandbox or a test host runs. Before anything changes it backs the app's data up (unless backup is false) and, unless check is false or this is a rollback, runs function.check over the version's functions and refuses with 400 and the diagnostics when one has an error. A version with a blob not uploaded cannot go live (upload what version.get lists in missing). When a function was deployed directly since the version now live was recorded and this would overwrite or delete it, direct controls what happens: refuse (the default) answers 409 DIRECT_CHANGES with direct_changes instead; replace applies the version anyway (reporting replaced); keep applies it except those functions, left as deployed (reporting kept). With sandbox it acts on that sandbox instead. With plan true it answers the plan of the change (each collection's shape change, the retypes and the documents they would read) and applies nothing. Answers live, previous, rollback_to, sandbox and backup_id, and direct_changes, replaced or kept when relevant", [
|
|
1678
|
+
route("POST", "/v1/_live", [], [], true),
|
|
1679
|
+
]),
|
|
1680
|
+
operation(633, "version.log", "The commits of a branch, newest first, following first parents: each one's id, number, generation, parents, author, when it was made and what it did. branch names the branch (default main) and limit the most to answer; next_cursor (null on the last page) is the cursor the next page passes. path answers only the commits that changed that one unit (the history of one function, route or file), newest first", [
|
|
1681
|
+
route("GET", "/v1/_commits", [], ["limit","cursor","branch","path"], false),
|
|
1682
|
+
]),
|
|
1683
|
+
operation(404, "version.restore", "Restore the app's data from the backup a promote took (the latest when none is named) and make live again the version that was live then", [
|
|
1684
|
+
route("POST", "/v1/_restore", [], [], true),
|
|
1685
|
+
]),
|
|
1686
|
+
operation(634, "version.show", "One commit's own record (the route's {version}: an id, a number on branch, or a branch name whose head it is): its root tree, its parents, its number, its generation, its author and what it did. units:true answers every path it holds as well", [
|
|
1687
|
+
route("GET", "/v1/_commits/{version}", ["version"], ["branch","units"], false),
|
|
1688
|
+
]),
|
|
1689
|
+
operation(636, "version.unit", "Read one unit (a file) out of a version (the route's {version}: an id, a number, a branch, live or a sandbox name; path the unit): text when the bytes are text, base64 when they are not, with the unit's size in bytes. start_line and end_line (exclusive) answer those lines only, with lines (the unit's line count); offset and length answer a byte range; max_bytes caps the text answered (truncated says it was cut)", [
|
|
1690
|
+
route("GET", "/v1/_commits/{version}/unit", ["version"], ["start_line","end_line","max_bytes","branch","path","offset","length"], false),
|
|
1691
|
+
]),
|
|
1692
|
+
operation(225, "view.create", "Create a view; see the schema for its definition. The id it answers is what view.update, view.delete and view.refresh take", [
|
|
1693
|
+
route("POST", "/v1/_views", [], [], true),
|
|
1694
|
+
]),
|
|
1695
|
+
operation(227, "view.delete", "Delete one view by its id (the route's {name}, as view.create returned it); view.list shows the views", [
|
|
1696
|
+
route("DELETE", "/v1/_views/{name}", ["name"], [], false),
|
|
1697
|
+
]),
|
|
1698
|
+
operation(319, "view.get", "Read materialized view data", [
|
|
1699
|
+
route("GET", "/v1/_views/{name}/data", ["name"], [], false),
|
|
1700
|
+
]),
|
|
1701
|
+
operation(224, "view.list", "List the app's views: materialized and live", [
|
|
1702
|
+
route("GET", "/v1/_views", [], [], false),
|
|
1703
|
+
]),
|
|
1704
|
+
operation(228, "view.refresh", "Recompute one materialized view now, by its id", [
|
|
1705
|
+
route("POST", "/v1/_views/{name}/refresh", ["name"], [], false),
|
|
1706
|
+
]),
|
|
1707
|
+
operation(226, "view.update", "Enable or disable one view: body name and enabled", [
|
|
1708
|
+
route("PATCH", "/v1/_views/{name}", ["name"], [], true),
|
|
1709
|
+
]),
|
|
1710
|
+
operation(230, "webhook.create", "Create a webhook route; see the schema for its body", [
|
|
1711
|
+
route("POST", "/v1/_webhooks", [], [], true),
|
|
1712
|
+
]),
|
|
1713
|
+
operation(232, "webhook.delete", "Delete one webhook route by its id (the route's {id}, as webhook.list shows it)", [
|
|
1714
|
+
route("DELETE", "/v1/_webhooks/{id}", ["id"], [], false),
|
|
1715
|
+
]),
|
|
1716
|
+
operation(295, "webhook.get", "Read one webhook route by its id (the route's {id}, as webhook.list shows it)", [
|
|
1717
|
+
route("GET", "/v1/_webhooks/{id}", ["id"], [], false),
|
|
1718
|
+
]),
|
|
1719
|
+
operation(229, "webhook.list", "List the app's webhook routes (optionally only those of one scope), oldest first, a page at a time (limit, cursor; next_cursor)", [
|
|
1720
|
+
route("GET", "/v1/_webhooks", [], ["scope","limit","cursor"], false),
|
|
1721
|
+
]),
|
|
1722
|
+
operation(296, "webhook.logs", "Read the delivery log of one webhook route by id: options limit", [
|
|
1723
|
+
route("GET", "/v1/_webhooks/{id}/logs", ["id"], ["limit"], false),
|
|
1724
|
+
]),
|
|
1725
|
+
operation(490, "webhook.replay", "Send a stored webhook delivery again under its same delivery id: the route's {id} is the delivery id (the <brand>-Delivery-Id header the receiver saw, or a dead letter's delivery_id). A delivered or dead delivery gets a new attempt count; a pending one is refused", [
|
|
1726
|
+
route("POST", "/v1/_webhooks/deliveries/{id}/replay", ["id"], [], false),
|
|
1727
|
+
]),
|
|
1728
|
+
operation(231, "webhook.update", "Enable or disable one webhook route: body id and enabled; the route's {id}", [
|
|
1729
|
+
route("PATCH", "/v1/_webhooks/{id}", ["id"], [], true),
|
|
1730
|
+
]),
|
|
1731
|
+
]);
|