Before you begin
Install curl and jq. You need the identifier of your existing Organization and an API key whose ServicePrincipal has authority to create the resources below. This customer workflow begins after Cantora has provisioned both; it does not run platform bootstrap.
These commands use Bash. Start a Bash session in this terminal and keep it open for every command below. Then enter the credential at a hidden prompt so its literal value does not enter shell history. Keep the key out of source code, Agent Configuration, command output, and logs.
bashread -rsp "Cantora API key: " CANTORA_API_KEY
printf '\n'
read -rp "Cantora Organization ID: " CANTORA_ORGANIZATION_ID
export CANTORA_API_KEY CANTORA_ORGANIZATION_ID
export CANTORA_API_ORIGIN="https://api.cantora.ai"curl --fail-with-body --silent --show-error \
"${CANTORA_API_ORIGIN}/v1/identity" \
--header "Authorization: Bearer ${CANTORA_API_KEY}" | jq1. Create a Project and Test Environment
A Project owns the Agent Definition and its immutable versions. The environments field creates the Test Environment in the same authorized operation.
PROJECT_RESPONSE=$(curl --fail-with-body --silent --show-error \
"${CANTORA_API_ORIGIN}/v1/organizations/${CANTORA_ORGANIZATION_ID}/projects" \
--header "Authorization: Bearer ${CANTORA_API_KEY}" \
--header "Content-Type: application/json" \
--data @- <<'JSON'
{
"name": "Customer support",
"environments": [
"test"
]
}
JSON
)
export CANTORA_PROJECT_ID=$(printf '%s' "${PROJECT_RESPONSE}" | jq -er '.projectId')
printf '%s\n' "${PROJECT_RESPONSE}" | jqList the Project's Environments to capture the generated Test identifier. The response is an array; select the member whose name is test.
ENVIRONMENTS_RESPONSE=$(curl --fail-with-body --silent --show-error \
"${CANTORA_API_ORIGIN}/v1/organizations/${CANTORA_ORGANIZATION_ID}/projects/${CANTORA_PROJECT_ID}/environments" \
--header "Authorization: Bearer ${CANTORA_API_KEY}")
export CANTORA_ENVIRONMENT_ID=$(printf '%s' "${ENVIRONMENTS_RESPONSE}" | \
jq -er '.[] | select(.name == "test") | .environmentId')
printf '%s\n' "${ENVIRONMENTS_RESPONSE}" | jq2. Create the Agent Definition
The key is stable across Environments and versions. The response records ApiManaged as the configuration authority and returns the Agent Definition identifier used by every later step.
DEFINITION_RESPONSE=$(curl --fail-with-body --silent --show-error \
"${CANTORA_API_ORIGIN}/v1/organizations/${CANTORA_ORGANIZATION_ID}/projects/${CANTORA_PROJECT_ID}/agent-definitions" \
--header "Authorization: Bearer ${CANTORA_API_KEY}" \
--header "Content-Type: application/json" \
--data @- <<'JSON'
{
"key": "support-agent",
"displayName": "Support Agent"
}
JSON
)
export CANTORA_AGENT_DEFINITION_ID=$(printf '%s' "${DEFINITION_RESPONSE}" | jq -er '.agentDefinitionId')
printf '%s\n' "${DEFINITION_RESPONSE}" | jq3. Publish an immutable Agent Version
The manifest declares complete behavior and finite budgets. The source object is visibly synthetic in this example; replace it with your repository, reviewed commit, configuration path, workflow, and run identifiers. It contains provenance, never a credential.
VERSION_RESPONSE=$(curl --fail-with-body --silent --show-error \
"${CANTORA_API_ORIGIN}/v1/organizations/${CANTORA_ORGANIZATION_ID}/projects/${CANTORA_PROJECT_ID}/agent-definitions/${CANTORA_AGENT_DEFINITION_ID}/versions" \
--header "Authorization: Bearer ${CANTORA_API_KEY}" \
--header "Content-Type: application/json" \
--data @- <<'JSON'
{
"manifest": {
"schemaVersion": "config.cantora.ai/agent-version/v1alpha2",
"instructions": "Answer support questions using the approved knowledge sources and cite the evidence used.",
"model": {
"provider": "googleVertex",
"model": "gemini-3.6-flash",
"settings": {
"thinkingLevel": "low"
}
},
"tools": [],
"requiredBindings": [],
"budgets": {
"maxModelSteps": 12,
"maxToolCalls": 0,
"totalWallTimeMs": 240000,
"modelStepTimeMs": 90000,
"streamStallTimeMs": 15000,
"maxInputTokens": 100000,
"maxOutputTokens": 16000,
"maxReasoningTokens": 32000,
"maxTotalTokens": 140000,
"maxToolResultBytes": 1000000,
"maxEstimatedCostMicros": 5000000
}
},
"source": {
"repository": "example.invalid/acme/support-agent",
"commit": "0000000000000000000000000000000000000000",
"path": "cantora/support-agent.json",
"workflow": "publish-support-agent",
"run": "example-1"
}
}
JSON
)
export CANTORA_AGENT_VERSION_ID=$(printf '%s' "${VERSION_RESPONSE}" | jq -er '.version.agentVersionId')
printf '%s\n' "${VERSION_RESPONSE}" | jqThe response outcome is created for new content or reused when the normalized manifest already exists. Both outcomes return the exact immutable version.
4. Provision the Test Agent
The Environment Agent is the runtime principal for this Agent Definition in Test. It begins at release generation zero with no current Agent Release, and the response includes the ETag required for activation.
AGENT_RESPONSE=$(curl --fail-with-body --silent --show-error \
"${CANTORA_API_ORIGIN}/v1/organizations/${CANTORA_ORGANIZATION_ID}/projects/${CANTORA_PROJECT_ID}/environments/${CANTORA_ENVIRONMENT_ID}/agents" \
--header "Authorization: Bearer ${CANTORA_API_KEY}" \
--header "Content-Type: application/json" \
--data @- <<JSON
{
"agentDefinitionId": "${CANTORA_AGENT_DEFINITION_ID}"
}
JSON
)
export CANTORA_AGENT_PRINCIPAL_ID=$(printf '%s' "${AGENT_RESPONSE}" | jq -er '.principalId')
export CANTORA_AGENT_RELEASE_ETAG=$(printf '%s' "${AGENT_RESPONSE}" | jq -er '.releaseEtag')
printf '%s\n' "${AGENT_RESPONSE}" | jq5. Activate the first Test Agent Release
Activation is an optimistic-concurrency-protected operation. If-Match carries the current ETag, and the body selects the immutable Agent Version with a durable reason and source record.
RELEASE_RESPONSE=$(curl --fail-with-body --silent --show-error \
"${CANTORA_API_ORIGIN}/v1/organizations/${CANTORA_ORGANIZATION_ID}/projects/${CANTORA_PROJECT_ID}/environments/${CANTORA_ENVIRONMENT_ID}/agents/${CANTORA_AGENT_PRINCIPAL_ID}/releases" \
--header "Authorization: Bearer ${CANTORA_API_KEY}" \
--header "Content-Type: application/json" \
--header "If-Match: ${CANTORA_AGENT_RELEASE_ETAG}" \
--data @- <<JSON
{
"agentVersionId": "${CANTORA_AGENT_VERSION_ID}",
"reason": "Activate the first tested support Agent Version.",
"source": {
"repository": "example.invalid/acme/support-agent",
"commit": "0000000000000000000000000000000000000000",
"path": "cantora/support-agent.json",
"workflow": "publish-support-agent",
"run": "example-1"
}
}
JSON
)
printf '%s\n' "${RELEASE_RESPONSE}" | jqThe returned release is append-only and affects new work only. This quickstart stops after Test activation; it does not claim or execute a Live promotion.
Next steps
- Read Agent Configuration before changing the manifest or activating another version.
- Use the API reference for complete schemas, constraints, and responses.
- Follow Errors when a request returns a validation, authority, conflict, or precondition failure.
Remove the key from the local environment when finished: unset CANTORA_API_KEY.