Retry API requests without creating duplicate databases
Agents retry when a request times out. Idempotency keys and asynchronous operations let them do so safely.
An agent that creates a database and then loses its network connection does not know whether the request succeeded. If it simply sends the request again, it may end up with two databases, two bills, and two sets of credentials to clean up.
The Lymeno API is designed so that retrying is always safe. This article explains the two mechanisms that make this possible: idempotency keys and asynchronous operations.
Send an idempotency key with every create request
POST /v1/databases and DELETE /v1/databases/:id accept an Idempotency-Key header. The key can be any string of 1–255 printable ASCII characters. Derive it from something your agent already tracks, such as a task ID, so that every retry of the same task sends the same key.
curl -X POST https://console.lymeno.com/v1/databases \
-H "Authorization: Bearer $LYMENO_TOKEN" \
-H "Idempotency-Key: task-142" \
-H "Content-Type: application/json" \
-d '{"workspaceId": "'$WORKSPACE_ID'", "regionId": "'$REGION_ID'", "name": "task-142", "productType": "serverless"}'
The first request returns 202 Accepted with an operationId, a databaseId, and the status queued. Here is what happens when the same key is sent again:
| Request | Response |
|---|---|
| Same key, same body | 200 OK with the original operationId and databaseId and the current status |
| Same key, same body, sent concurrently | Every request receives the same operation |
| Same key, different body | 409 with the error code conflict |
Keys are scoped to a workspace and an endpoint. Two agents in the same workspace should therefore never reuse a key for different tasks, and a key used to create a database does not affect a later delete request.
Wait for the operation, not the request
Creating, resizing, and deleting a database take longer than an HTTP request should stay open. Each of these changes is an operation that you poll until it finishes:
curl https://console.lymeno.com/v1/operations/$OPERATION_ID \
-H "Authorization: Bearer $LYMENO_TOKEN"
An operation moves through queued and running and ends as succeeded, failed, or cancelled. The response also lists each step with its own status and timestamps, so an agent can report progress instead of waiting silently.
When the operation succeeds, request the connection details:
curl https://console.lymeno.com/v1/databases/$DATABASE_ID/connection \
-H "Authorization: Bearer $LYMENO_TOKEN"
The response contains the hostname, the database name, the ports, and sslmode: verify-full. The hostname is null until the database is ready.
Check before you retry a role
Creating a database role needs extra care. It does not accept an idempotency key, because the response contains the role’s password, which Lymeno returns only once and does not store in plain text. A replay could not return the same password.
If a request to POST /v1/databases/:id/roles times out, call GET /v1/databases/:id/roles first. If the role already exists, reset its password with POST /v1/databases/:id/roles/:roleId/reset-password instead of creating it again.
Handle errors by code, not by message
Every error has the same shape:
{ "error": { "code": "conflict", "message": "…", "details": { "reason": "…" } } }
code is one of unauthorized, forbidden, not_found, validation_failed, conflict, payment_required, or internal. details.reason adds a more specific cause, such as name_taken or operation_in_progress. Messages are written for people and may change. Codes and reasons are stable, so build your agent’s retry and recovery logic on them.
A simple rule covers most cases: retry internal errors and network failures with the same idempotency key, and treat every other code as a decision for the agent to make.