Every API here is available over the APIs.io API and to AI agents over MCP.
openapi: 3.2.0
info:
title: orq.ai Identities API
version: '2.0'
description: orq.ai API documentation
servers:
- url: https://my.orq.ai
security:
- ApiKey: []
tags:
- name: Identities
description: Identities represent end users from your system for usage and engagement tracking.
paths:
/v2/identities:
get:
tags:
- Identities
summary: List identities
description: Retrieves a paginated list of identities in your workspace. Use pagination parameters to navigate through large identity lists efficiently.
operationId: ListIdentities
parameters:
- name: limit
in: query
schema:
type: integer
format: int32
- name: starting_after
in: query
description: "Cursor for forward pagination. Set to the `_id` of the last item from\n the previous page."
schema:
type: string
- name: ending_before
in: query
description: "Cursor for backward pagination. Set to the `_id` of the first item from\n the previous page."
schema:
type: string
- name: search
in: query
description: Case-insensitive search text matched against identity profile fields.
schema:
type: string
- name: filter_by.tags
in: query
description: Return only identities that have at least one of these tags.
schema:
type: array
items:
type: string
- name: include_metrics
in: query
description: Include aggregate usage metrics on each returned identity.
schema:
type: boolean
- name: sort_by
in: query
description: Field used to order the list.
schema:
$ref: '#/components/schemas/IdentitySortField'
- name: include_budget
in: query
description: "When true, embed each identity's identity-scoped budget (config and\n limits only, no live usage) on the returned records. Adds one budget\n lookup for the page; omit to skip it."
schema:
type: boolean
responses:
'200':
description: OK
content:
application/json:
schema:
$ref: '#/components/schemas/ListIdentitiesResponse'
'401':
description: Authentication required. See https://docs.orq.ai/reference/errors for the error codes.
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
x-speakeasy-group: identities
x-speakeasy-name-override: list
x-cli-group: identities
x-cli-name: list
x-code-samples:
- lang: curl
label: Core - List identities by tag
source: "curl --get 'https://my.orq.ai/v2/identities' \\\n --header \"Authorization: Bearer $ORQ_API_KEY\" \\\n --data-urlencode 'limit=25' \\\n --data-urlencode 'search=acme' \\\n --data-urlencode 'filter_by.tags=enterprise' \\\n --data-urlencode 'include_metrics=true'\n"
- lang: python
label: Python - List identities by tag
source: "import os\nfrom orq_ai_sdk import Orq\n\nclient = Orq(api_key=os.environ[\"ORQ_API_KEY\"])\n\npage = client.identities.list(\n limit=25,\n search=\"acme\",\n filter_by={\"tags\": [\"enterprise\"]},\n include_metrics=True,\n)\n\nfor identity in page.data:\n print(identity.external_id, identity.display_name)\n"
- lang: typescript
label: Node.js - List identities by tag
source: "import { Orq } from '@orq-ai/node';\n\nconst client = new Orq({\n apiKey: process.env.ORQ_API_KEY,\n});\n\nconst page = await client.identities.list({\n limit: 25,\n search: 'acme',\n filterBy: {\n tags: ['enterprise'],\n },\n includeMetrics: true,\n});\n\nfor (const identity of page.data) {\n console.log(identity.externalId, identity.displayName);\n}\n"
post:
tags:
- Identities
summary: Create an identity
description: Creates a new identity with a unique external_id. If an identity with the same external_id already exists, the operation will fail.
operationId: CreateIdentity
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/CreateIdentityRequest'
required: true
responses:
'201':
description: Identity created successfully.
headers:
Location:
description: Relative URI of the created identity.
schema:
type: string
content:
application/json:
schema:
$ref: '#/components/schemas/CreateIdentityResponse'
'401':
description: Authentication required. See https://docs.orq.ai/reference/errors for the error codes.
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
x-speakeasy-group: identities
x-speakeasy-name-override: create
x-cli-group: identities
x-cli-name: create
x-code-samples:
- lang: curl
label: Core - Create customer identity
source: "curl --request POST \\\n --url 'https://my.orq.ai/v2/identities' \\\n --header \"Authorization: Bearer $ORQ_API_KEY\" \\\n --header 'Content-Type: application/json' \\\n --data '{\n \"external_id\": \"customer_12345\",\n \"display_name\": \"Ada Lovelace\",\n \"email\": \"ada@example.com\",\n \"tags\": [\"enterprise\", \"beta\"],\n \"metadata\": {\n \"plan\": \"enterprise\",\n \"region\": \"eu\"\n }\n }'\n"
- lang: python
label: Python - Create customer identity
source: "import os\nfrom orq_ai_sdk import Orq\n\nclient = Orq(api_key=os.environ[\"ORQ_API_KEY\"])\n\nresult = client.identities.create(\n external_id=\"customer_12345\",\n display_name=\"Ada Lovelace\",\n email=\"ada@example.com\",\n tags=[\"enterprise\", \"beta\"],\n metadata={\n \"plan\": \"enterprise\",\n \"region\": \"eu\",\n },\n)\n\nprint(result.identity.external_id)\n"
- lang: typescript
label: Node.js - Create customer identity
source: "import { Orq } from '@orq-ai/node';\n\nconst client = new Orq({\n apiKey: process.env.ORQ_API_KEY,\n});\n\nconst result = await client.identities.create({\n externalId: 'customer_12345',\n displayName: 'Ada Lovelace',\n email: 'ada@example.com',\n tags: ['enterprise', 'beta'],\n metadata: {\n plan: 'enterprise',\n region: 'eu',\n },\n});\n\nconsole.log(result.identity.externalId);\n"
- lang: curl
label: Core - Create minimal identity
source: "curl --request POST \\\n --url 'https://my.orq.ai/v2/identities' \\\n --header \"Authorization: Bearer $ORQ_API_KEY\" \\\n --header 'Content-Type: application/json' \\\n --data '{\n \"external_id\": \"user_9f4b2a\"\n }'\n"
- lang: python
label: Python - Create minimal identity
source: "import os\nfrom orq_ai_sdk import Orq\n\nclient = Orq(api_key=os.environ[\"ORQ_API_KEY\"])\n\nresult = client.identities.create(\n external_id=\"user_9f4b2a\",\n)\n\nprint(result.identity.external_id)\n"
- lang: typescript
label: Node.js - Create minimal identity
source: "import { Orq } from '@orq-ai/node';\n\nconst client = new Orq({\n apiKey: process.env.ORQ_API_KEY,\n});\n\nconst result = await client.identities.create({\n externalId: 'user_9f4b2a',\n});\n\nconsole.log(result.identity.externalId);\n"
/v2/identities/{id}:
get:
tags:
- Identities
summary: Retrieve an identity
description: Retrieves detailed information about a specific identity using their identity ID or external ID from your system.
operationId: RetrieveIdentity
parameters:
- name: id
in: path
description: Identity ID to retrieve.
required: true
schema:
type: string
- name: include_metrics
in: query
description: Include aggregate usage metrics on the returned identity.
schema:
type: boolean
- name: include_budget
in: query
description: "When true, embed the identity-scoped budget (config and limits only,\n no live usage) on the returned record."
schema:
type: boolean
responses:
'200':
description: OK
content:
application/json:
schema:
$ref: '#/components/schemas/RetrieveIdentityResponse'
'401':
description: Authentication required. See https://docs.orq.ai/reference/errors for the error codes.
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
'404':
description: The resource does not exist. See https://docs.orq.ai/reference/errors for the error codes.
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
x-speakeasy-group: identities
x-speakeasy-name-override: retrieve
x-cli-group: identities
x-cli-name: retrieve
x-code-samples:
- lang: curl
label: Core - Retrieve identity with metrics
source: "curl --get 'https://my.orq.ai/v2/identities/customer_12345' \\\n --header \"Authorization: Bearer $ORQ_API_KEY\" \\\n --data-urlencode 'include_metrics=true'\n"
- lang: python
label: Python - Retrieve identity with metrics
source: "import os\nfrom orq_ai_sdk import Orq\n\nclient = Orq(api_key=os.environ[\"ORQ_API_KEY\"])\n\nresult = client.identities.retrieve(\n id=\"customer_12345\",\n include_metrics=True,\n)\n\nprint(result.identity.display_name)\nprint(result.identity.metrics.total_requests)\n"
- lang: typescript
label: Node.js - Retrieve identity with metrics
source: "import { Orq } from '@orq-ai/node';\n\nconst client = new Orq({\n apiKey: process.env.ORQ_API_KEY,\n});\n\nconst result = await client.identities.retrieve({\n id: 'customer_12345',\n includeMetrics: true,\n});\n\nconsole.log(result.identity.displayName);\nconsole.log(result.identity.metrics.totalRequests);\n"
delete:
tags:
- Identities
summary: Delete an identity
description: Permanently deletes an identity from your workspace and cleans up associated budget configurations.
operationId: DeleteIdentity
parameters:
- name: id
in: path
description: Identity ID to delete.
required: true
schema:
type: string
responses:
'200':
description: OK
content:
application/json:
schema:
$ref: '#/components/schemas/DeleteIdentityResponse'
'401':
description: Authentication required. See https://docs.orq.ai/reference/errors for the error codes.
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
x-speakeasy-group: identities
x-speakeasy-name-override: delete
x-cli-group: identities
x-cli-name: delete
x-code-samples:
- lang: curl
label: Core - Delete identity
source: "curl --request DELETE \\\n --url 'https://my.orq.ai/v2/identities/customer_12345' \\\n --header \"Authorization: Bearer $ORQ_API_KEY\"\n"
- lang: python
label: Python - Delete identity
source: "import os\nfrom orq_ai_sdk import Orq\n\nclient = Orq(api_key=os.environ[\"ORQ_API_KEY\"])\n\nclient.identities.delete(\n id=\"customer_12345\",\n)\n"
- lang: typescript
label: Node.js - Delete identity
source: "import { Orq } from '@orq-ai/node';\n\nconst client = new Orq({\n apiKey: process.env.ORQ_API_KEY,\n});\n\nawait client.identities.delete({\n id: 'customer_12345',\n});\n"
patch:
tags:
- Identities
summary: Update an identity
description: Updates specific fields of an existing identity. Only the fields provided in the request body will be updated.
operationId: UpdateIdentity
parameters:
- name: id
in: path
description: Identity ID to update.
required: true
schema:
type: string
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/UpdateIdentityRequest'
required: true
responses:
'200':
description: OK
content:
application/json:
schema:
$ref: '#/components/schemas/UpdateIdentityResponse'
'401':
description: Authentication required. See https://docs.orq.ai/reference/errors for the error codes.
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
x-speakeasy-group: identities
x-speakeasy-name-override: update
x-cli-group: identities
x-cli-name: update
x-code-samples:
- lang: curl
label: Core - Update identity profile
source: "curl --request PATCH \\\n --url 'https://my.orq.ai/v2/identities/customer_12345' \\\n --header \"Authorization: Bearer $ORQ_API_KEY\" \\\n --header 'Content-Type: application/json' \\\n --data '{\n \"display_name\": \"Ada L.\",\n \"tags\": [\"enterprise\", \"priority\"],\n \"metadata\": {\n \"plan\": \"enterprise\",\n \"lifecycle\": \"renewal\"\n }\n }'\n"
- lang: python
label: Python - Update identity profile
source: "import os\nfrom orq_ai_sdk import Orq\n\nclient = Orq(api_key=os.environ[\"ORQ_API_KEY\"])\n\nresult = client.identities.update(\n id=\"customer_12345\",\n display_name=\"Ada L.\",\n tags=[\"enterprise\", \"priority\"],\n metadata={\n \"plan\": \"enterprise\",\n \"lifecycle\": \"renewal\",\n },\n)\n\nprint(result.identity.display_name)\n"
- lang: typescript
label: Node.js - Update identity profile
source: "import { Orq } from '@orq-ai/node';\n\nconst client = new Orq({\n apiKey: process.env.ORQ_API_KEY,\n});\n\nconst result = await client.identities.update({\n id: 'customer_12345',\n displayName: 'Ada L.',\n tags: ['enterprise', 'priority'],\n metadata: {\n plan: 'enterprise',\n lifecycle: 'renewal',\n },\n});\n\nconsole.log(result.identity.displayName);\n"
components:
schemas:
BudgetPeriod:
type: string
enum:
- BUDGET_PERIOD_DAILY
- BUDGET_PERIOD_WEEKLY
- BUDGET_PERIOD_MONTHLY
- BUDGET_PERIOD_YEARLY
- BUDGET_PERIOD_ONE_TIME
DeleteIdentityResponse:
type: object
properties: {}
ProviderBudgetScope:
type: object
properties:
provider:
type: string
description: "Per-provider cap. The value is the provider enum string (e.g.\n \"openai\", \"anthropic\") drawn from ModelIntegrationIdentifier."
IdentitySortField:
type: string
enum:
- IDENTITY_SORT_FIELD_DISPLAY_NAME
- IDENTITY_SORT_FIELD_UPDATED
ApiKeyBudgetScope:
type: object
properties:
api_key_id:
type: string
description: "Per-api-key cap. Replaces the legacy embedded `constraints.budget`\n associated with an API key."
ErrorResponse:
type: object
properties:
message:
type: string
description: Human-readable error message.
code:
type: integer
description: gRPC status code, when the service reports one.
details:
type: array
items: {}
description: Additional error details, when the service reports any.
required:
- message
CreateIdentityRequest:
required:
- external_id
type: object
properties:
external_id:
type: string
description: "Customer-provided stable identifier for this identity. Must be unique\n within the workspace."
display_name:
type: string
description: Human-readable display name for the identity.
email:
type: string
description: Email address associated with the identity.
avatar_url:
type: string
description: URL of the identity avatar image.
tags:
type: array
items:
type: string
description: Free-form labels used to organize and filter identities.
metadata:
type: object
additionalProperties: true
description: Custom JSON metadata stored with the identity.
ModelBudgetScope:
required:
- model_id
type: object
properties:
model_id:
type: string
description: "Per-model cap. The value is the FULL model reference as callers send\n it (\"openai/gpt-4o\", or \"workspaceKey@openai/gpt-4o\" for private\n models)."
Identity:
required:
- _id
- external_id
- workspace_id
- created
- updated
type: object
properties:
_id:
type: string
description: "Unique identity identifier assigned by ORQ. Returned as `_id` for\n compatibility with the v1 identity API."
external_id:
type: string
description: "Customer-provided stable identifier for this identity. This value is\n required on create and is unique within the workspace."
workspace_id:
type: string
description: Workspace that owns the identity.
display_name:
type: string
description: Human-readable display name for the identity.
email:
type: string
description: Email address associated with the identity.
avatar_url:
type: string
description: URL of the identity avatar image.
tags:
type: array
items:
type: string
description: Free-form labels used to organize and filter identities.
metadata:
type: object
additionalProperties: true
description: Custom JSON metadata stored with the identity.
created:
type: string
description: ISO timestamp for when the identity was created.
updated:
type: string
description: ISO timestamp for when the identity was last updated.
metrics:
allOf:
- $ref: '#/components/schemas/IdentityMetrics'
description: "Optional usage and cost metrics. Present only when requested with\n `include_metrics`."
budget:
readOnly: true
allOf:
- $ref: '#/components/schemas/Budget'
description: "The budget scoped to this identity, if one exists. Read-only here:\n budgets are created and managed through the Budgets API\n (scope.identity). Present only when requested with `include_budget`.\n Live consumption (`usage`) is not attached on this path — call the\n Budgets API for current spend."
IdentityMetrics:
required:
- total_tokens
- total_cost
- total_requests
- error_rate
type: object
properties:
total_tokens:
type: number
description: Total token count attributed to the identity.
format: double
total_cost:
type: number
description: Total cost attributed to the identity.
format: double
total_requests:
type: number
description: Total request count attributed to the identity.
format: double
error_rate:
type: number
description: Fraction of attributed requests that failed.
format: double
ListIdentitiesResponse:
required:
- object
- data
- has_more
type: object
properties:
object:
type: string
description: Object discriminator for list responses; always `list`.
data:
type: array
items:
$ref: '#/components/schemas/Identity'
description: Page of identities.
has_more:
type: boolean
description: "Whether more identities are available in the selected pagination\n direction."
BudgetScope:
type: object
properties:
workspace:
$ref: '#/components/schemas/WorkspaceBudgetScope'
project:
$ref: '#/components/schemas/ProjectBudgetScope'
identity:
$ref: '#/components/schemas/IdentityBudgetScope'
api_key:
$ref: '#/components/schemas/ApiKeyBudgetScope'
provider:
$ref: '#/components/schemas/ProviderBudgetScope'
model:
$ref: '#/components/schemas/ModelBudgetScope'
description: Closed oneof of workspace, project, identity, api_key, provider, or model. Exactly one variant must be set.
BudgetUsage:
type: object
properties:
amount:
type: number
format: double
tokens:
type: number
description: "Carried as a double (not int64) so it serializes as a JSON number\n rather than a quoted string, matching limits.token_limit."
format: double
requests:
type: integer
format: int32
description: "BudgetUsage is the latest available current-period consumption used\n for budget enforcement, not an exact billing ledger. Each dimension is\n the consumed side of the matching limit dimension: `amount` is the\n accumulated cost in USD (vs limits.amount), `tokens` is the accumulated\n token count (vs limits.token_limit), and `requests` is the count in the\n rolling 60-second window (vs rate_limit.requests_per_minute). All three\n are explicit-presence so the triple is always emitted in full, zeros\n included — a never-spent budget serializes {amount:0, tokens:0,\n requests:0} rather than dropping its zero dimensions."
WorkspaceBudgetScope:
type: object
properties: {}
description: Workspace-wide ceiling. The implicit target is the caller's workspace.
CreateIdentityResponse:
required:
- identity
type: object
properties:
identity:
allOf:
- $ref: '#/components/schemas/Identity'
description: Newly created identity.
RateLimit:
type: object
properties:
requests_per_minute:
type: integer
format: int32
description: "Per-minute request ceiling applied to the requests this budget\n matches."
ProjectBudgetScope:
type: object
properties:
project_id:
type: string
description: Per-project cap.
BudgetLimits:
type: object
properties:
period:
$ref: '#/components/schemas/BudgetPeriod'
amount:
type: number
format: double
token_limit:
type: number
description: "Token ceiling for the budget period. Token counts are whole numbers\n and stored as integers."
format: double
description: "BudgetLimits is the per-period spend and token ceiling. At least one\n of `amount`, `token_limit`, or RateLimit.requests_per_minute MUST be\n set on a Budget; that invariant is enforced by the handler."
BudgetAlert:
required:
- threshold_percent
- notifier_ids
type: object
properties:
id:
type: string
description: Assigned by ORQ. Supply an existing id to edit that alert in place.
threshold_percent:
type: integer
description: Percentage of the dimension's limit at which the alert fires, 1–100.
format: uint32
notifier_ids:
type: array
items:
type: string
description: Must be workspace-scoped; project-scoped notifiers are rejected.
dimension:
description: Defaults to COST when unset.
$ref: '#/components/schemas/BudgetAlertDimension'
description: "Notifies every listed notifier once current-period consumption on\n `dimension` reaches `threshold_percent` of the matching limit. Each alert\n fires at most once per period, re-arming on rollover or a limit change.\n The dimension must have a limit set, or the write is rejected. A TOKENS alert is rejected."
UpdateIdentityResponse:
required:
- identity
type: object
properties:
identity:
allOf:
- $ref: '#/components/schemas/Identity'
description: Updated identity.
BudgetAlertDimension:
type: string
enum:
- BUDGET_ALERT_DIMENSION_COST
- BUDGET_ALERT_DIMENSION_TOKENS
UpdateIdentityRequest:
required: []
type: object
properties:
display_name:
type: string
description: New display name. Omit to keep the current display name.
email:
type: string
description: New email address. Omit to keep the current email.
avatar_url:
type: string
description: New avatar image URL. Omit to keep the current avatar URL.
tags:
type: array
items:
type: string
description: Replacement tag list. Leave empty to clear tags.
metadata:
type: object
additionalProperties: true
description: Replacement custom JSON metadata.
IdentityBudgetScope:
type: object
properties:
identity_external_id:
type: string
description: "Per-identity cap. Keyed by the contact's external identifier so the\n scope is stable across imports."
RetrieveIdentityResponse:
required:
- identity
type: object
properties:
identity:
allOf:
- $ref: '#/components/schemas/Identity'
description: Requested identity.
BudgetMatch:
type: object
properties:
cel:
type: string
description: 'CEL expression that decides whether a budget applies to a request. Available variables: `model`, `provider`, `model_id`, `api_key`, `identity`, `project`, `metadata` (map), `headers` (map, lowercase keys). An empty expression always matches. Expressions are syntax-validated at write time.'
Budget:
required:
- budget_id
- limits
- created_at
- updated_at
type: object
properties:
budget_id:
type: string
scope:
allOf:
- $ref: '#/components/schemas/BudgetScope'
description: "Scope the budget was created for, used to group and filter budgets in\n the console. Not used for matching: the `match` expression decides\n which requests a budget applies to. Unset for budgets created from a\n raw CEL expression (\"custom\")."
match:
allOf:
- $ref: '#/components/schemas/BudgetMatch'
description: "The matching semantics of the budget. A budget applies to a request\n when `match.cel` evaluates to true against the request context.\n Creating a budget for a scope derives the expression for that scope\n (e.g. `provider == \"openai\"`); an empty expression matches every\n request in the workspace."
limits:
$ref: '#/components/schemas/BudgetLimits'
rate_limit:
$ref: '#/components/schemas/RateLimit'
is_active:
type: boolean
expires_at:
type: string
format: date-time
created_at:
type: string
format: date-time
updated_at:
type: string
format: date-time
usage:
allOf:
- $ref: '#/components/schemas/BudgetUsage'
description: "Latest available consumption used for enforcement in the current\n period. This is not an exact billing ledger. Populated by read paths\n (Get / List); omitted on write responses (Create / Update / Reset)\n where it carries no signal. Absent or all-zero for a budget that\n has not been spent against in the current period."
alerts:
type: array
items:
$ref: '#/components/schemas/BudgetAlert'
description: Threshold notifications. Absent when the budget has none.
description: Budget defines limits and matching rules used to govern consumption.
securitySchemes:
ApiKey:
type: http
scheme: bearer
bearerFormat: JWT
externalDocs:
url: https://docs.orq.ai
description: orq.ai Documentation
x-speakeasy-timeout: 600000