# The product API with curl > Find your organization and Test environment, register an agent identity, retry a change safely and read errors, with curl. Section: Synapse and Locus. Web page: https://docs.katara.com/products/curl/ These requests call the Synapse and Locus API directly. Availability depends on the services enabled for your environment. The examples use `curl` and `jq`, and keep your key and the IDs they find in shell variables, so each block can be pasted as it stands, in order. ## Before you start Create a Test key on the platform's **API Keys** page with these scopes: - `org:read` to find the organization and its environment - `org:service_accounts:write` to create a service account - `locus:grants:read` and `locus:grants:write` to register and list agent identities Put the key in `KATARA_API_KEY` from wherever you keep secrets, rather than typing it into your shell history, and set the API origin: ```sh export KATARA_API=https://api.katara.com ``` ## Find the organization and environment A key belongs to one organization and one environment, so the organization list holds a single entry: ```sh curl -sS "$KATARA_API/katara/v1/orgs" \ -H "Authorization: Bearer $KATARA_API_KEY" ``` ```json { "data": [ { "id": "0c73f1f4-5356-4253-b75c-9e5afe17f413", "name": "Northwind Labs", "role": "owner", "version": 2, "permissions": ["locus:grants:read", "locus:grants:write", "org:read", "org:service_accounts:write"] } ] } ``` `permissions` is what this key may do there: its scopes, bounded by your current role. Keep the organization ID and the ID of its Test environment: ```sh export KATARA_ORG=$(curl -sS "$KATARA_API/katara/v1/orgs" \ -H "Authorization: Bearer $KATARA_API_KEY" | jq -r '.data[0].id') export KATARA_ENV=$(curl -sS "$KATARA_API/katara/v1/orgs/$KATARA_ORG/environments" \ -H "Authorization: Bearer $KATARA_API_KEY" | jq -r '.data[] | select(.mode == "test") | .id') ``` Read the environment: ```sh curl -sS "$KATARA_API/katara/v1/orgs/$KATARA_ORG/environments/$KATARA_ENV" \ -H "Authorization: Bearer $KATARA_API_KEY" ``` ```json { "org_id": "0c73f1f4-5356-4253-b75c-9e5afe17f413", "environment_id": "1dde350c-d18c-47ad-b665-ef1266c29419", "id": "1dde350c-d18c-47ad-b665-ef1266c29419", "mode": "test", "authority_epoch": 1, "reset_generation": 1, "state": "active" } ``` `authority_epoch` and `reset_generation` identify the state you read. A change that depends on them sends both back, and the server refuses it if either has moved on. ## Create a service account Every change carries an `Idempotency-Key` of 8–128 letters, digits, dots, underscores, colons or hyphens. Use a new key for each new change, and the same key only to retry the same request. ```sh export KATARA_SERVICE_ACCOUNT=$(curl -sS -X POST "$KATARA_API/katara/v1/orgs/$KATARA_ORG/service-accounts" \ -H "Authorization: Bearer $KATARA_API_KEY" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: curl-guide-service-account-1" \ -d '{"name": "Document assistant"}' | jq -r '.resource_id') ``` A change answers with an operation receipt, and `resource_id` is the ID of what it created: ```json { "operation_id": "848660a3-c0f0-420c-b88d-da4e85711e7d", "resource_id": "050b433c-9ad0-40b5-a95b-e34d07ab7105", "completed": true } ``` ## Register an agent identity An agent identity binds the service account to this environment. Build the request from the environment you read a moment ago, so its pins are current, and keep it in a file: ```sh curl -sS "$KATARA_API/katara/v1/orgs/$KATARA_ORG/environments/$KATARA_ENV" \ -H "Authorization: Bearer $KATARA_API_KEY" | jq '{expected_environment_epoch: .authority_epoch, expected_reset_generation: .reset_generation, name: "Document assistant", service_account_id: env.KATARA_SERVICE_ACCOUNT, execution_kind: "managed"}' > identity.json ``` Send it: ```sh curl -sS -X POST "$KATARA_API/katara/v1/orgs/$KATARA_ORG/environments/$KATARA_ENV/agent-identities" \ -H "Authorization: Bearer $KATARA_API_KEY" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: curl-guide-agent-identity-1" \ -d @identity.json ``` ```json { "operation_id": "7887df99-f4bc-410c-b18e-b8e624565c25", "resource_id": "a681edf0-757c-4a3e-994f-2e2dbf643120", "completed": true } ``` List the environment's identities: ```sh curl -sS "$KATARA_API/katara/v1/orgs/$KATARA_ORG/environments/$KATARA_ENV/agent-identities" \ -H "Authorization: Bearer $KATARA_API_KEY" ``` ```json { "data": [ { "org_id": "0c73f1f4-5356-4253-b75c-9e5afe17f413", "environment_id": "1dde350c-d18c-47ad-b665-ef1266c29419", "id": "a681edf0-757c-4a3e-994f-2e2dbf643120", "service_account_id": "050b433c-9ad0-40b5-a95b-e34d07ab7105", "name": "Document assistant", "execution_kind": "managed", "active": true, "authority_epoch": 1, "created_by": "7c8dfbbc-ac06-494d-a3f5-4698009b63a4", "created_at": "2026-09-30T21:29:58.132458Z" } ] } ``` An identity alone publishes no definition, starts no worker and grants no spending authority. [Agent identities](https://docs.katara.com/products/agent-identities.txt) and [Runs](https://docs.katara.com/products/runs.txt) continue from here. ## Retry a change safely When a reply is lost, send the identical request with the same key. The server answers with the original receipt and changes nothing: ```sh curl -sS -X POST "$KATARA_API/katara/v1/orgs/$KATARA_ORG/environments/$KATARA_ENV/agent-identities" \ -H "Authorization: Bearer $KATARA_API_KEY" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: curl-guide-agent-identity-1" \ -d @identity.json ``` The `operation_id` and `resource_id` are the ones the first reply carried. ## Read errors Every error has the same shape. `error.code` says what happened, `error.request_id` identifies the request if you contact support, and `error.action`, when present, says what to do next. A used key sent with a different body is refused, however small the difference: ```sh jq '.name = "Research assistant"' identity.json | curl -sS -X POST "$KATARA_API/katara/v1/orgs/$KATARA_ORG/environments/$KATARA_ENV/agent-identities" \ -H "Authorization: Bearer $KATARA_API_KEY" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: curl-guide-agent-identity-1" \ -d @- ``` ```json { "error": { "message": "The resource changed or the idempotency key was used for another request.", "type": "conflict_error", "code": "conflict", "param": null, "request_id": "hc_8b80b327c41049a999396e52", "upstream_status": null, "action": "refresh" } } ``` `refresh` means read the current state and review the change again before sending it under a new key. A stale `expected_environment_epoch` answers the same way. A request without a key, or with a revoked or expired one, answers `401`. In a script, `--fail-with-body` makes curl exit with status 22 on an error while still printing the body: ```sh curl -sS --fail-with-body "$KATARA_API/katara/v1/orgs/$KATARA_ORG/environments" ``` ```json { "error": { "message": "missing or invalid session", "type": "authentication_error", "code": "unauthorized", "param": null, "request_id": "hc_51d8cf6de148b50220144963", "upstream_status": null } } ``` [Access and API keys](https://docs.katara.com/products/access.txt) lists the scopes for other tasks, and the product API reference describes every route these requests use.