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
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
RESTPATCH /v1/workspace
CLIlath workspace rename [--name] [--slug]
DashboardSettings → Workspace
not available on MCP
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
RESTPATCH /v1/project
CLIlath project rename [--name] [--slug]
DashboardSettings → Project
not available on MCP
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
RESTPOST /v1/auth/return-origins
CLIlath auth allow-origin <origin>
DashboardSettings → Authentication
not available on MCP
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
RESTDELETE /v1/auth/return-origins
CLIlath auth revoke-origin <origin>
DashboardSettings → Authentication
not available on MCP
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
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.
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
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
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
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
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
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
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
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
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, with Lath's platform fee attached, 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. THE FEE IS NOT AN INPUT and cannot be changed once the payment exists.
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
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 reverses Lath's fee at the rate THAT charge paid rather than at today's rate. The month the original charge landed in is the month the volume comes back out of. 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
RESTPOST /v1/payments/charges/:charge/refund
MCPlath.payment.refund
CLIlath payment refund <charge> [--amount]
not available on Dashboard
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
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
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
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
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
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
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
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
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
autoRenewboolean?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
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
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
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
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
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
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
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.
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
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
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
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
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
RESTPOST /v1/platform/budget/account_quota
MCPlath.platform.budget.sync_account_quota
CLIlath platform budget account-quota <account_daily_quota>
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.hosting.deploy
Postgreslath.database.migration.run
Authenticationregistered · no agent surface yet
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.