ath/DocsBack to lath.com

Operations

Everything Lath can be asked to do, and where each can be asked.

This is not a reference written about the API. It is the registry itself — the same module the REST layer routes from, the MCP server advertises as tools, and the dashboard derives its controls from.

There is one source. If an operation gains a capability or loses a surface, this page changes because the registry changed, not because somebody remembered to update a page. A build check fails if any screen names an operation instead of importing it.

account.createworkspaceno mode
Creates the first project inside a workspace that already exists. The person, their session, the workspace and the owner membership all precede it: an operation cannot create the thing it is scoped to, and operations.md §6c is why.
no reversal
projectstringthe id of the project to create. GENERATED BY THE DOOR, never accepted from a client — this operation declares no transport surface, so there is no client that could send one
Refuses with nothing of its own.
DashboardSignup
not available on REST, MCP, CLI
workspace.renameworkspaceno mode
Changes the workspace's display name, its slug, or both. THE OLD SLUG IS RELEASED IMMEDIATELY and may be taken by anyone — safe today because no URL, read or route contains a slug. operations.md §6d records what changes when one does.
no reversal
namestring?the display name; omitted leaves it alone
slugstring?lowercase, digits and single hyphens; unique across all workspaces
Refuses with workspace_not_found, workspace_required.
RESTPATCH /v1/workspace
MCPlath.workspace.rename
CLIlath workspace rename [--name] [--slug]
DashboardSettings → Workspace
project.renameprojectno mode
Changes the project's display name, its slug, or both. The slug is unique within the workspace rather than globally, and the old one is released immediately — see workspace.rename.
no reversal
namestring?the display name; omitted leaves it alone
slugstring?lowercase, digits and single hyphens; unique within the workspace
Refuses with project_not_found.
RESTPATCH /v1/project
MCPlath.project.rename
CLIlath project rename [--name] [--slug]
DashboardSettings → Project
auth.allow_return_originprojectlive / test
Permits one origin to receive end users after an OAuth sign-in. Origins are matched EXACTLY — there are no wildcards, no prefix rules and no subdomain rules, because an open redirect on a session-bearing callback always arrives as a matching rule somebody thought was tight. Registering an origin that is already registered changes nothing and is not an error.
no reversal
originstringscheme, host and optional port — https://app.example.com. A path is dropped
Refuses with origin.
RESTPOST /v1/auth/return-origins
MCPlath.auth.allow_return_origin
CLIlath auth allow-origin <origin>
DashboardSettings → Authentication
auth.revoke_return_originprojectlive / test
Removes one origin from the allow-list. End users already signed in are unaffected — their sessions are rows and outlive this — but no further sign-in may be returned to that origin.
no reversal
originstringthe origin to remove, in the form it was registered
Refuses with origin, origin_not_registered.
RESTDELETE /v1/auth/return-origins
MCPlath.auth.revoke_return_origin
CLIlath auth revoke-origin <origin>
DashboardSettings → Authentication
invite.sendworkspaceno mode
Invites an email address to join this workspace at a role, and sends them a single-use link that expires in seven days. The address may have no Lath account; the account is resolved when the invitation is accepted, not when it is sent.
no reversal
emailstringthe address to invite. Normalised lowercase; plus-addressing is kept, because a+lath@x is a distinct address somebody chose
rolestringadmin | developer | billing | readonly. NOT owner — transferring ownership is its own act with its own confirmation
Refuses with already_a_member, email.
RESTPOST /v1/invites
CLIlath invites send <email> --role <role>
DashboardTeam
not available on MCP
plan.createprojectlive / test
Defines a plan and the features it grants, in one act. Features are named by key: a key that already exists in this project is referenced rather than duplicated, and a key that exists with a different type or period is refused rather than redefined. Bumps the project's access definition version, which makes every previously compiled entitlement row unreachable.
no reversal
slugstringproject-local identifier, unique among plans
namestring
descriptionstring?
featuresarrayat least one — {key, type, value} with optional name and period. A plan with no features grants nothing, so an empty list is refused.
Refuses with duplicate_feature, feature_conflict, plan_key_taken, scope_violation.
RESTPOST /v1/plans
MCPlath.plan.create
CLIlath plan create <slug> <name> --features [--description]
DashboardRevenue → Products & Prices → New plan
key.createworkspaceno mode
Issues an API key for this workspace and returns its value ONCE. The value is never stored — only a SHA-256 hash and the prefix — so no read, no screen and no later call can show it again, and the operations row does not carry it. The key may call exactly the operations and reads named in scopes, frozen at issue: a key does not gain an operation registered after it was cut. Naming a project confines the key to it; omitting one issues a workspace key that reaches every project in the workspace.
no reversal
namestringwhat this key is for, as a person would say it — "Deploy bot", "CI runner". It is the label Developer Access orders by and the thing somebody reads during an incident, so a key called "key" is a key nobody can revoke with confidence
scopesstring[]the operation and read keys this key may call — a subset of the two registries, never invented names. It must also be a subset of what the person issuing it may do themselves: a key cannot carry authority its creator does not have
modestringlive | test. It fixes the key's prefix and the only commercial mode it may act in. Declared as an input rather than read from the request because this operation is mode-less — it records NULL — so there is no caller mode for it to inherit
projectstring?confine the key to one project. Omitted issues a WORKSPACE key that reaches every project in the workspace, which developer.md §5 renders as "CI runner … workspace … reaches 6 projects"
environmentsstring[]?confine the key to these environments. Omitted means unrestricted, which is a different claim from an empty list and is why the column is nullable rather than defaulted
Refuses with human_required, mode_mismatch, project_not_found, scope_exceeds_role, unknown_scope.
RESTPOST /v1/keys
CLIlath keys create <name> --scopes --mode [--project] [--environments]
DashboardDeveloper Access
not available on MCP
key.revokeworkspaceno mode
Stops a key authenticating, immediately and permanently. The row stays and is marked with the operation that revoked it, so Activity can render who did it and when; the key's value is not recoverable, because it was never stored. Revoking an already-revoked key is refused rather than ignored — it would overwrite which operation did it.
no reversal
keystringthe key's id, not its value or its prefix. Developer Access and the project Keys tab both render it, and a caller reading a value off a compromised machine is the one situation this operation exists for — so the id is what a person has, and the value is what they do not
Refuses with already_revoked, key_not_found.
RESTPOST /v1/keys/:key/revoke
MCPlath.key.revoke
CLIlath keys revoke <key>
DashboardDeveloper Access
access.grantprojectlive / test
Adds to the customer's entitlement for one feature. The grant is a ladder step above plan, so the effective value becomes plan + grant. It does not replace the plan value.
undo
customerstring
featurestring
valuenumberADDED to the plan value, not a replacement for it
expires_attimestamp?when omitted the grant does not expire
reasonstring?recorded on the operations row and shown in Activity
Refuses with customer_not_found, expires_at, feature_not_found, value_shape.
RESTPOST /v1/access/grants
MCPlath.access.grant
CLIlath access grant <customer> <feature> <value> [--expires-at] [--reason]
DashboardCustomer detail → Grant access
access.override.setenvironmentlive / test
Switches a feature off for EVERY customer in this environment and mode, immediately. It sits above the plan and above grants: a customer whose plan includes the feature, and who holds a grant for it, is still denied. It can only take away — there is no setting that turns a feature on. Reverse it with access.override.clear, which names the row this returns; the operations row does not carry an entitlement snapshot, because what changed is not shaped like one customer.
no reversal
featurestringthe feature KEY. An id is not accepted: a key is resolved inside the project, so another tenant's feature cannot be named here
reasonstring?recorded on the operations row and shown in Activity
Refuses with already_overridden, feature_not_found.
RESTPOST /v1/access/overrides
MCPlath.access.override.set
CLIlath access override set <feature> [--reason]
not available on Dashboard
access.override.clearenvironmentlive / test
Lifts an override, so the plan and any grants decide again. The override row is kept and marked rather than deleted — an override that ended is what explains an entitlement that changed.
no reversal
overridestring
reasonstring?recorded on the operations row and shown in Activity
Refuses with already_cleared, override_not_found.
RESTDELETE /v1/access/overrides/:override
MCPlath.access.override.clear
CLIlath access override clear <override>
not available on Dashboard
access.revokeprojectlive / test
Removes the grant immediately. The customer's effective value falls back to plan plus any other grants.
undo
grantstring
reasonstring?recorded on the operations row and shown in Activity
Refuses with already_revoked, grant_not_found.
RESTDELETE /v1/access/grants/:grant
MCPlath.access.revoke
CLIlath access revoke <grant>
DashboardAccess → Grants → Revoke
subscription.createprojectlive / test
Subscribes a customer to a plan, so every feature the plan grants becomes part of that customer's entitlement immediately. This is what makes a plan mean anything: until a customer is subscribed, a plan is a definition nothing reads. Sets no billing period — nothing bills yet — and does not charge.
retry
customerstringmust exist in this project and mode
planstringmust exist in this project. Plans carry no mode: a plan is a definition, and what is live or test is the subscription
sourcestring?which channel took the money — card | app_store | play_store | manual. Absent records manual, which is what a subscription nobody paid for IS: this operation charges nothing. The value names a CHANNEL and never a supplier, and it changes nothing about what the customer may do — entitlement is compiled from the plan and the grants, and no part of that compilation reads it. It exists so "where did this subscription come from" is a stored fact rather than an inference from configuration
external_refstring?the source store's own identifier for this purchase. Absent for a subscription nobody paid for. It is never Lath's identity for the subscription and never appears on a public surface — the id this operation returns is the only identity there is
Refuses with customer_not_found, plan_not_found.
RESTPOST /v1/subscriptions
MCPlath.subscription.create
CLIlath subscription create <customer> <plan> [--source] [--external-ref]
DashboardRevenue → Customers → Subscribe
subscription.cancelprojectlive / test
Stops the subscription renewing. Entitlement continues until the period ends unless cancelled immediately, and no refund is issued either way — refund is a separate operation.
undo
subscriptionstring
at_period_endboolean?true keeps access until the period ends; false ends entitlement now
reasonstring?recorded on the operations row and shown in Activity
Refuses with already_cancelled, no_period_end, not_cancellable, subscription_not_found.
RESTPOST /v1/subscriptions/:subscription/cancel
MCPlath.subscription.cancel
CLIlath subscription cancel <subscription> [--at-period-end] [--reason]
DashboardCustomer detail → Billing → Cancel
payment.chargeprojectlive / test
Creates a payment on the connected account for this amount and returns the credential the customer's own client uses to pay it. NO MONEY MOVES UNTIL THE CUSTOMER CONFIRMS — this is not a completed payment and must not be reported as one. It does NOT change entitlement: access and payment are separate ladders, the same way payment.refund does not.
no reversal
customerstring
amountnumberminor units, a whole number. 1000 is ten pounds, not one thousand pounds and not 10.00 — a fractional amount is refused rather than rounded, because rounding decides which of two readings the caller meant
currencystringISO 4217, three letters
Refuses with currency, customer, no_connected_account.
RESTPOST /v1/payments/charges
MCPlath.payment.charge
CLIlath payment charge <customer> --amount --currency
not available on Dashboard
payment.refundprojectlive / test
Returns money against a charge, in whole or in part, and records the cumulative amount returned on the charge itself. It does NOT change entitlement — access and payment are separate ladders, and a refunded customer keeps access until a grant or subscription change removes it.
no reversal
chargestringthe charge to refund, as returned by payment.charge
amountnumber?minor units, a whole number, and the same rule payment.charge states: a fractional amount is refused rather than rounded. Omit for a full refund of what is left; partial refunds are allowed and accumulate
Refuses with already_refunded, charge_not_found, charge_unconfirmed, no_connected_account, refund_amount, refund_exceeds_charge.
RESTPOST /v1/payments/charges/:charge/refund
MCPlath.payment.refund
CLIlath payment refund <charge> [--amount]
not available on Dashboard
environment.createprojectno mode
Creates an environment in this project — a name, a type and the mode traffic in it defaults to. Nothing is provisioned: databases, services and variables are created separately and each names an environment.
retry
namestring
typestring
default_modestring
Refuses with nothing of its own.
RESTPOST /v1/environments
MCPlath.environment.create
CLIlath environment create <name> --type --default-mode
DashboardSettings → Project → New environment
service.createenvironmentno mode
Creates a service in this environment — a name and a runtime. Nothing is created at a supplier: a service is a record here until a deployment uploads a build for it, and the name a build is uploaded under is derived from this service's id at that point.
retry
namestring
runtimestring
Refuses with nothing of its own.
RESTPOST /v1/hosting/services
MCPlath.service.create
CLIlath service create <name> --runtime
DashboardHosting → New service
hosting.deployenvironmentno mode
Builds and deploys the commit to the service, replacing what is serving once verification passes. Traffic does not move until it does.
retry · replay · cancel
servicestring
commitstring
Refuses with commit_sha, service_not_found.
RESTPOST /v1/hosting/deployments
MCPlath.hosting.deploy
CLIlath deploy <service> --commit
DashboardHosting → Deploy
variable.setenvironmentno mode
Stores a value for this environment, or for one service in it, encrypted under a key held per project. IT DOES NOT TAKE EFFECT UNTIL THE NEXT DEPLOY: variables are materialised into a deployment, so what is running now keeps the values it was given — and from that deploy on the value exists in the running service as well as here. AND THERE IS NO DEPLOY YET. Hosting is not built, so nothing reads these values today: the value is stored, encrypted, for a consumer that does not exist. There is no rename: the stored value is bound to its own name, so moving it would be a new secret rather than the same one. Set the new name and remove the old.
retry · replay
keystringletters, digits and underscore, not starting with a digit — the name a runtime reads. Setting a name that already exists replaces its value
valuestringat most 4096 characters. Never returned by any read: a variable can be replaced but not revealed
servicestringoptional. Absent means the whole environment; present means this service only, and a service from another environment is refused
Refuses with bad_data_key, service_not_found.
RESTPUT /v1/variables/:key
MCPlath.variable.set
CLIlath variable set <key> <value> --service
not available on Dashboard
variable.removeenvironmentno mode
Deletes the variable and its stored value. The value is not kept anywhere and cannot be recovered — set it again to have one. LIKE SETTING, IT DOES NOT TAKE EFFECT UNTIL THE NEXT DEPLOY: a running service keeps the value it was deployed with, and the deploy after this one will not have it. AND THERE IS NO DEPLOY YET — hosting is not built, so no running service has ever been given this value.
retry
keystringthe name to remove. Refused if nothing is set at that name and scope, rather than reported as done
servicestringoptional, and it must match how the variable was set — an environment-wide variable is not removed by naming a service
Refuses with variable_not_found.
RESTDELETE /v1/variables/:key
MCPlath.variable.remove
CLIlath variable remove <key> --service
not available on Dashboard
vault.key.revokeprojectno mode
Ends every encryption key this project has, permanently. Every value stored for this project stops being readable — in every environment at once, live and test alike, because the key is held per project and the values are held per environment. NOTHING IS DELETED AND NOTHING IS RECOVERABLE: the stored values stay where they are and can no longer be opened, so the way back is to set each name again, which mints a new key on the first one. IT DOES NOT REACH ANYTHING ALREADY RUNNING: a deploy hands a service its values as bindings, and revoking the key they were sealed under leaves that service holding them. AND THERE IS NO DEPLOY YET. Hosting is not built, so nothing has ever been handed these values and there is nothing running for this not to reach. There is no undo.
no reversal
projectstringthe id of the project whose key is being revoked. The project is already known from the credential; naming it is the confirmation, and a request naming a different one is refused
Refuses with already_revoked, no_data_key, project_mismatch.
RESTPOST /v1/vault/key/revoke
MCPlath.vault.key.revoke
CLIlath vault revoke <project>
not available on Dashboard
database.migration.runenvironmentno mode
Runs the migration's statements against the environment's database. There is no inverse and no recovery point: until backups exist, rolling this back is something you do with your own migration.
retry
migrationstringmust not already be applied in this environment; re-running is refused, not repeated
Refuses with already_applied, migration_not_found.
RESTPOST /v1/database/migrations/:migration/run
MCPlath.database.migration.run
CLIlath db migrate <migration>
DashboardDatabase → Migrations → Run
database.createenvironmentno mode
Provisions a database for this environment. It returns immediately and the database is not ready yet — creation takes several minutes, and the database's own status is what says when it is usable. No credential is issued here.
no reversal
engine_versionstringthe engine version, e.g. 18.4
regionstringwhere it runs; a latency and residency fact
backup_retention_daysnumberdays of point-in-time recovery, 7-35. Required with no default: the underlying default is 1 day and that is a trap rather than a choice
max_capacitynumberthe ceiling in capacity units; supplied from the plan, never defaulted
Refuses with nothing of its own.
RESTPOST /v1/database/instances
MCPlath.database.create
CLIlath db create --engine-version --region --backup-retention-days --max-capacity
DashboardDatabase → Create
database.credential.rotateenvironmentno mode
Issues a new password for the database's owner role and returns it once. The previous password stops working immediately. Lath stores no password, so this is also how the first one is issued — and the only way to replace a lost one.
retry
databasestringthe database instance in this environment
Refuses with bad_identifier, database_not_available, database_not_found, sql_error.
RESTPOST /v1/database/instances/:database/rotate_credential
MCPlath.database.credential.rotate
CLIlath db rotate-credential <database>
DashboardDatabase → Connection → Rotate
mailbox.createprojectno mode
Creates a mailbox on a domain this project holds. THE BOX EXISTS BEFORE MAIL CAN REACH IT — receiving needs an MX record pointing at the mail host, which is a separate act on the zone, so a new mailbox reports its MX state as pending rather than claiming to work. It does NOT require the domain to be verified for sending: that proves DNS control for outbound and a mailbox is the receiving side.
retry
domainstringthe domain this project holds
addressstringthe full address; it must be on that domain, because the MX that would make it work belongs to that domain's zone
Refuses with address_not_on_domain, address_shape, already_exists, domain_not_found, invalid, no_response, throttled, unavailable.
RESTPOST /v1/domains/:domain/mailboxes
MCPlath.mailbox.create
CLIlath mailboxes create <domain> <address>
DashboardDomains → Mailboxes → Add
domain.registerprojectno mode
Registers the domain with Lath's registrar and records it against this project. THE REGISTRATION IS BILLABLE AND RENEWS YEARLY unless auto-renew is turned off. The row exists before the registrar is called, so a domain whose outcome is unknown is visible rather than absent — it is the registrar's answer, not this call, that decides whether the name is held.
no reversal
namestringthe domain to register, lowercased — a name differing only by case is the same name to DNS
auto_renewboolean?defaults to true; a domain that lapses because nobody set this is the failure this service prevents
privacyboolean?defaults to true; off publishes the registrant's address in WHOIS
Refuses with already_exists, domain_name, invalid, no_response, throttled, unavailable.
RESTPOST /v1/domains
MCPlath.domain.register
CLIlath domains register <name>
DashboardDomains → Add
domain.record.updateprojectno mode
Replaces the record's value in the zone. The zone is project-scoped, so the change applies to every environment bound to this domain.
retry
domainstring
recordstringthe DNS record being replaced
valuestringthe new value; replaces the record's contents entirely
Refuses with domain_not_found, invalid, no_response, record_not_found, record_not_in_domain, record_not_managed, throttled, unavailable.
RESTPATCH /v1/domains/:domain/records/:record
MCPlath.domain.record.update
CLIlath domain record update <domain> <record> <value>
DashboardDomains → DNS → Edit
customer.createprojectlive / test
Creates a customer in this project with one email identifier, which is how a person who signed up inside the customer's own product comes to exist here. The identifier is UNVERIFIED — nothing in this call proves the address belongs to anyone. The customer has no entitlement until a plan or a grant gives them one; creating is not granting.
no reversal
emailstringthe address the person signed up with. Stored as an identifier, not as identity — a customer may hold several, and changing one does not create a new customer
namestring?optional; a customer without one is a real row
Refuses with email, email_taken.
RESTPOST /v1/customers
MCPlath.customer.create
CLIlath customers create <email> [--name]
not available on Dashboard
customer.disableprojectlive / test
Denies all entitlement for the customer immediately — every feature answers not-allowed while the customer is disabled, whatever their plan and grants say. Those are left UNTOUCHED, so re-enabling restores exactly what they had. Billing is unaffected: a disabled customer with an active subscription keeps being charged, which is a condition Overview surfaces deliberately.
undo
customerstring
Refuses with already_disabled, customer_not_found.
RESTPOST /v1/customers/:customer/disable
MCPlath.customer.disable
CLIlath customer disable <customer>
DashboardCustomer detail → Disable
email.resendprojectlive / test
Sends the same message again to the same recipient. It does not re-render the template, so the content is what was originally sent.
retry · replay
messagestring
RESTPOST /v1/email/messages/:message/resend
CLIlath email resend <message>
DashboardEmail → Messages → Resend
not available on MCP
hosting.rollbackenvironmentno mode
Deploys a previous build again. It is a new deployment with its own operations row, never a mutation of the deployment history.
retry
deploymentstring
Refuses with deployment_not_found.
RESTPOST /v1/hosting/deployments/:deployment/rollback
MCPlath.hosting.rollback
CLIlath rollback <deployment>
DashboardHosting → Deployments → Roll back
webhook.endpoint.createenvironmentlive / test
Registers a URL to receive this environment's events and mints its first signing secret. THE SECRET IS RETURNED ONCE AND NEVER AGAIN — no read, no screen and no later call can show it, and the operations row does not carry it. The endpoint is bound to the environment the caller named; an endpoint covering every environment in a project is a shape the schema allows and no operation creates.
no reversal
urlstringhttps, with no credentials and no fragment. A loopback or private address is refused in every mode: a delivery to one leaves and arrives on Lath own network, and the delivery status code is readable by the customer
eventsstring[]the declared event names to subscribe to. An undeclared name is refused rather than stored — a subscription that can never match receives nothing and never says so
Refuses with environment_required, unknown_event, url.
RESTPOST /v1/webhooks/endpoints
MCPlath.webhook.endpoint.create
CLIlath webhooks endpoint create <url> --events
DashboardSettings → Webhooks → Add endpoint
webhook.endpoint.rotate_secretenvironmentlive / test
Issues a new signing secret and marks the current one retiring. BOTH SIGN EVERY DELIVERY FOR 24 HOURS, so a receiver can move at its own pace and nothing breaks at the moment of rotation; the old one stops signing when that window closes, by comparison at signing time rather than by a scheduled job. Rotating again inside an open window is refused — receivers are told to expect two signatures, not three. The new secret is returned once and never again.
no reversal
endpointstringthe endpoint whose secret to rotate. It must be in the caller project, mode AND environment — the last of those is what stops a key scoped to one environment from breaking another receiver
Refuses with endpoint_not_found, no_active_secret, rotation_in_progress.
RESTPOST /v1/webhooks/endpoints/:endpoint/rotate_secret
MCPlath.webhook.endpoint.rotate_secret
CLIlath webhooks endpoint rotate-secret <endpoint>
DashboardSettings → Webhooks → Rotate signing secret
email.identity.createprojectno mode
Registers a domain this project will sign mail for. The identity starts unverified and holds NO sending allowance — DNS has not been published yet, and the database refuses an allowance on an unverified identity. Publishing the records and passing the check comes next; the allowance is a separate operation after that. The account holds a limited number of sending domains, and one is reserved for Lath's own mail — so this can be refused for capacity even when the domain itself is fine.
retry
domainstringthe bare domain to sign mail for — example.com, with no scheme, path or address part. One domain is one identity across the whole platform, because its DNS can only say one thing
Refuses with domain_capacity, domain_taken, invalid_domain, workspace_domain_limit.
RESTPOST /v1/email/identities
MCPlath.email.identity.create
CLIlath email identity create <domain>
not available on Dashboard
email.identity.verifyprojectno mode
Looks up this identity's DNS records and records WHAT WAS ACTUALLY FOUND against each one, then sets the identity's state from the result and the state it was in before. A domain that was verified and now fails reaches `drifted` rather than `pending` — different cause, different urgency — AND LOSES ITS SENDING ALLOWANCE, because the database refuses to hold an allowance on anything unverified. Recovering it is a separate grant, not a resumption. Retryable and safe to repeat: a probe asks and records, and asking twice records the second answer.
retry
identitystringthe sending identity to check
Refuses with identity_has_no_records, identity_not_found.
RESTPOST /v1/email/identities/:identity/verify
MCPlath.email.identity.verify
CLIlath email identity verify <identity>
not available on Dashboard
email.identity.quota.setprojectno mode
Sets how many messages a day this identity may send. The account has one daily limit shared by every customer, and a fixed part of it is reserved for Lath's own mail and cannot be allocated — so a request that would reach into the reserve is REFUSED BY THE DATABASE, not by a check, and the refusal names the headroom.
retry
identitystringthe sending identity to allocate to
daily_quotaintegermessages per rolling 24 hours. The name says the unit because the number alone does not — this is a daily allowance, not a rate
Refuses with identity_not_found, identity_not_verified, no_change, reserve_breach.
RESTPOST /v1/email/identities/:identity/quota
MCPlath.email.identity.quota.set
CLIlath email identity quota <identity> <daily_quota>
not available on Dashboard
platform.budget.set_reserveplatformno mode
Sets how much of the account's daily sending limit is held back for Lath's own mail — sign-in links, invites, recovery — and can never be allocated to a customer. Raising it past what is already allocated is refused. It SEEDS AT THE ENTIRE LIMIT, so the first customer allowance cannot be granted until this is lowered deliberately.
retry
reserveintegermessages per rolling 24 hours to hold back. Zero is legal and means Lath's own mail competes with customers' for the same limit, which is the state this control exists to prevent
Refuses with no_change, reserve_breach.
RESTPOST /v1/platform/budget/reserve
MCPlath.platform.budget.set_reserve
CLIlath platform budget reserve <reserve>
not available on Dashboard
platform.budget.sync_account_quotaplatformno mode
Records the account's daily sending limit as the provider now reports it. This is the SAME BREACH FROM THE OTHER SIDE: a reduction below what is already allocated is refused, and the refusal names which identities to cut first. Refusing does not undo the provider's change — it refuses to record a limit the allocation model cannot satisfy, which would leave every later allocation failing for an invisible reason.
retry
account_daily_quotaintegermessages per rolling 24 hours, READ FROM THE PROVIDER rather than chosen. A guess here is a reserve computed against a ceiling that does not exist
Refuses with no_change, quota_below_allocation.
RESTPOST /v1/platform/budget/account_quota
MCPlath.platform.budget.sync_account_quota
CLIlath platform budget account-quota <account_daily_quota>
not available on Dashboard
account.connectworkspaceno mode
Creates the account money settles into, for this workspace or for one project in it. IT IS THE PRECONDITION FOR EVERY CHARGE — `payment.charge` refuses with `no_connected_account` until this has run and been confirmed. TWO PHASES: the intent is recorded first with no provider account behind it, then the provider is asked. A failed second phase leaves the row present and unconfirmed rather than absent, so a retry finds the intent it already made instead of making a second one. THE COUNTRY CANNOT BE CHANGED afterwards and neither can who collects fees and losses — that is the provider's rule, recorded in migration 0020 with its sources, and a different country means a different account rather than an edit.
no reversal
countrystringISO 3166-1 alpha-2, and PERMANENT. A country that cannot settle is refused here rather than at the first payout
emailstringwhere the provider sends account notices
projectstring?omit for a workspace account, which every project shares and is the common case. Naming one asks for a separate account for it — 0020 says this is asked and never inferred
Refuses with already_connected, forbidden, invalid_request, not_found, rate_limited, upstream.
RESTPOST /v1/connect/accounts
MCPlath.account.connect
CLIlath connect account <country> <email> [--project]
DashboardBilling → Connect
platform.send.set_capplatformno mode
Moves the hourly ceiling on Lath's own sending, without a deploy — 0019 records why it is a row rather than a constant. Raising it spends more of the provider's allowance per hour; lowering it refuses sends sooner. It does NOT stop sending: that is the kill switch, and the two are separate controls because an incident and a capacity change are separate decisions.
retry
hourly_capintegersends per hour, a whole number above zero
Refuses with no_change.
RESTPOST /v1/platform/send/cap
MCPlath.platform.send.set_cap
CLIlath platform send cap <hourly_cap>
not available on Dashboard
platform.send.disableplatformno mode
STOPS EVERY SEND, immediately and for every customer — sign-in links included, which means nobody can sign in while it is pulled. THE REASON IS REQUIRED BY THE DATABASE rather than by politeness: 0019's CHECK pairs it with the timestamp, because a switch thrown with no reason is one nobody can safely throw back. Undo is `platform.send.enable`, which acts on the same row using only what is already stored.
no reversal
reasonstringwhy sending stopped, in words the person who has to decide whether to release it will read at 3am
Refuses with already_disabled.
RESTPOST /v1/platform/send/disable
MCPlath.platform.send.disable
CLIlath platform send disable <reason>
not available on Dashboard
platform.send.enableplatformno mode
Releases the kill switch and lets sending resume. Clears the reason with it, so the row says what it means: a breaker that is not pulled carries no explanation for a state it is not in. Releasing a switch that is not pulled is refused rather than ignored — it would overwrite which operation released it.
retry
reasonstringwhy it is safe to resume — recorded on the operations row and read by whoever asks later why sending was off
Refuses with not_disabled.
RESTPOST /v1/platform/send/enable
MCPlath.platform.send.enable
CLIlath platform send enable <reason>
not available on Dashboard
export.createprojectlive / test
Writes every exportable table in this project, for this mode, as one file each, with the columns and types read from the live schema rather than from a document about it. SEALED TABLES ARE NOT WRITTEN and redacted ones lose named columns — a signing secret, a wrapped data key, a key hash and every external fulfilment reference stay behind, and the manifest says which and why rather than omitting them silently. It includes the OPERATIONS LEDGER, which is what says the rest is complete: 17 of 69 tables carry the operation that created each row, so every file states what vouches for it instead of one blanket claim. Exports nothing that changes, and is recorded anyway — an export is the widest read a caller can perform and belongs in Activity.
no reversal
modestringlive or test. A full export is one call per mode: mixing them would produce files whose rows mean two different things
Refuses with ambiguous_anchor, everything_withheld, ledger_absent, mode_mismatch, no_anchor, nothing_planned, sealed_table_planned, table_has_no_columns, withheld_column_absent, withhold_names_another_table.
RESTPOST /v1/export
MCPlath.export.create
CLIlath export <mode>
not available on Dashboard

What is missing is shown

An operation that is not exposed on a surface says so, rather than simply not appearing there. A gap you can see is a gap someone can ask about; a gap that is quietly absent is one that never gets closed.

What an agent can call, by service

The nine services Lath provisions, and whether each one is reachable as an agent tool today. Derived from the registry above, not written alongside it — a service with no operation registered says so.

Domains and DNSlath.domain.register
Mailboxeslath.mailbox.create
Hostinglath.service.create
Postgreslath.database.migration.run
Authenticationlath.auth.allow_return_origin
Paymentslath.subscription.cancel
Transactional emaillath.email.identity.create
Analyticsno operation yet
Error monitoringno operation yet
Capabilities — undo, retry, replay, restore, cancel — are properties of the operation, resolved by the server. A client never asserts what an operation can do; it asks.