Building reliable custom server actions

Protect data, avoid duplicate side effects and test public endpoints without assuming unlimited or atomic execution.

Protect custom server actions with permission checks, duplicate protection and error handling. Test retries and simultaneous requests before using them with customers.

Sign-in and permissions

For actions that need a user, require authentication and derive identity from the verified request. Do not accept a browser-supplied user ID as proof of permission. Keep data access scoped to the intended user and check the required role before sensitive actions.

An endpoint's sign-in setting, its code and the table's access rules all matter. Test a signed-out caller and two different users, including attempts to read or change another user's record. See Table access.

Writes, retries and notifications

Several await calls are not automatically one database transaction. If the first write succeeds and the next call fails, do not assume the first write was rolled back. A timeout can also happen after work has completed.

Before a repeatable action, decide how your app recognizes the same operation. Use a stable operation identifier and a server-enforced uniqueness rule where appropriate, then test two simultaneous requests. A “check whether it exists, then create” sequence alone can race. Verify any locking or atomic-update guarantee before relying on it.

Track an outgoing email or notification separately from the record that caused it. Distinguish “queued” or “accepted” from “delivered.” Check what completed before retrying, especially when money or external messages are involved.

Public tools and slow providers

A public form is exposed to repeated and automated requests. Ask the builder to add input validation, a reasonable request limit, clear errors and duplicate protection before it triggers costly work. A browser button being disabled is not a server-side limit.

Keep private provider keys in server-side Env Vars. Handle timeouts and rate-limit responses; do not advertise unlimited requests or one timeout value for every endpoint, outside provider and task. For slow work, consider saving progress and processing it in a scheduled task, with a clear completion state and retry rules. See Scheduling automatic updates.

Test without contacting real customers

Preview is not a no-send environment. It shares the app's data with the published version, and outbound actions can be real. Use synthetic records and your own recipient accounts. If you need a dry-run mode, implement and test it explicitly in the app; do not assume the platform supplies one for every custom endpoint.

For a failed action, follow Troubleshooting a custom server endpoint.

Was this helpful?

Thanks for the signal — it helps us improve the docs.

More in Advanced

Using Overskill from AI agents (Claude, Cursor, Codex)

Connect your assistant to Overskill with the hosted MCP URL and browser sign-in. Check the connected team, access and credits before building.

Getting started with the Overskill partner API

Generate, manage, and deploy apps programmatically. Auth, the core call sequence, and working code in Node, Python, and curl.

Overskill API errors and how to handle them

What 401s, rate limits, credit exhaustion, and other API errors look like — and the right way for your code (or your agent) to respond.

Still need help?

If this didn't answer your question, our team is one click away.