> For the complete documentation index, see [llms.txt](https://docs.ojin.ai/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.ojin.ai/getting-started/api.md).

# REST API

Use the REST API to manage persistent Ojin resources such as model configurations, assets, and Human Agents.

The raw OpenAPI specification for this API is also available at [openapi.gitbook.com/o/V9IIQ3Cw10PlDcbzN32h/spec/ojin-rest-api.json](https://openapi.gitbook.com/o/V9IIQ3Cw10PlDcbzN32h/spec/ojin-rest-api.json).

{% hint style="info" %}
This page covers the HTTP REST API. It is separate from Ojin's realtime model APIs, which use WebSockets for streaming media. For example, for `ojin/oris-portrait`, see [Realtime API Reference](/models/introduction/api.md).
{% endhint %}

## Authentication

Authenticated REST endpoints require an API key in the `X-API-Key` header.

```bash
curl https://api.ojin.ai/v1/model-configs \
  -H "X-API-Key: $OJIN_API_KEY"
```

For API key setup guidance, see [Get your API key](/getting-started/authentication.md).

## Model Configurations

Use model configurations to create and manage reusable settings for Ojin models.

`model_configurations` is variant-specific. To determine the correct object shape, first fetch the target model variant and read its `configuration_schema`, then build your `model_configurations` payload to match that JSON Schema.

## List Model Configurations

> Retrieves a list of Model Configurations. By default returns only organization-owned configurations. Use source='template' to retrieve template configurations instead. Supports pagination and filtering by model variant.

```json
{"openapi":"3.0.0","info":{"title":"Ojin REST API","version":"v1.0.0"},"tags":[{"name":"Model Configurations","description":"Operations related to user-defined configurations of model variants for API clients and product integrations."}],"servers":[{"url":"https://api.ojin.ai/v1","description":"Main backend server, version 1."}],"security":[{"APIKeyAuth":[]}],"components":{"securitySchemes":{"APIKeyAuth":{"type":"apiKey","in":"header","name":"X-API-Key","description":"API key for authenticated access to the Ojin REST API."}},"parameters":{"ModelVariantIdQueryParameter":{"name":"model_variant_id","in":"query","required":false,"description":"Filter model configurations by the model_variant_id.","schema":{"type":"string"}},"ModelVariantTagQueryParameter":{"name":"model_variant_tag","in":"query","required":false,"description":"Filter model configurations whose model variant contains this tag (e.g. \"_agent-tts\").","schema":{"type":"string"}},"SourceQueryParameter":{"name":"source","in":"query","required":false,"description":"Filter items by ownership source. Use 'org' for items owned by the user's organization, 'template' for items from the template organization. Defaults to 'org'.","schema":{"type":"string","enum":["org","template"],"default":"org"}},"LimitQueryParameter":{"name":"limit","in":"query","required":false,"description":"Number of items to return per page.","schema":{"type":"integer","default":20,"minimum":1,"maximum":100}},"OffsetQueryParameter":{"name":"offset","in":"query","required":false,"description":"Number of items to skip for pagination.","schema":{"type":"integer","default":0,"minimum":0}}},"schemas":{"ModelConfig":{"type":"object","description":"Represents a specific configuration of a ModelVariant.","properties":{"model_config_id":{"type":"string","format":"uuid","readOnly":true,"description":"Server-generated unique ID."},"organization_id":{"type":"string","readOnly":true,"description":"Owning organization ID (from external IdP)."},"model_id":{"type":"string","readOnly":true,"description":"ID of the parent Model, derived from the Model Variant."},"model_variant_id":{"type":"string","description":"ID of the ModelVariant being configured. NOT NULL."},"created_by":{"type":"string","readOnly":true,"description":"Creator's user ID (from external IdP), or `api_key:<id>` when created via an API key."},"title":{"type":"string","description":"Title for the configuration. NOT NULL, unique per organization_id."},"model_configurations":{"type":"object","additionalProperties":true,"description":"Parameters for the model variant, adhering to its schema. NOT NULL, defaults to '{}'.","default":{}},"created_at":{"type":"string","format":"date-time","readOnly":true,"description":"Timestamp of creation. Defaults to CURRENT_TIMESTAMP."},"updated_at":{"type":"string","format":"date-time","readOnly":true,"description":"Timestamp of last update. Auto-updates."}},"required":["model_config_id","organization_id","model_id","model_variant_id","created_by","title","model_configurations","created_at","updated_at"]},"Pagination":{"type":"object","properties":{"limit":{"type":"integer","description":"The number of items returned in the current page."},"offset":{"type":"integer","description":"The number of items skipped before starting the current page."},"total_items":{"type":"integer","description":"The total number of items available that match the query."}},"required":["limit","offset","total_items"]},"ErrorResponse":{"type":"object","properties":{"code":{"type":"string","description":"A short, machine-readable error code string."},"message":{"type":"string","description":"A human-readable description of the error."},"details":{"type":"object","additionalProperties":true,"nullable":true,"description":"Optional. Additional structured details about the error."}},"required":["code","message"]}},"responses":{"BadRequestError":{"description":"Invalid request payload or parameters.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"UnauthorizedError":{"description":"Authentication token is missing, invalid, or expired.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"ForbiddenError":{"description":"Authenticated principal does not have permission.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"paths":{"/model-configs":{"get":{"tags":["Model Configurations"],"summary":"List Model Configurations","description":"Retrieves a list of Model Configurations. By default returns only organization-owned configurations. Use source='template' to retrieve template configurations instead. Supports pagination and filtering by model variant.","operationId":"listModelConfigs","parameters":[{"$ref":"#/components/parameters/ModelVariantIdQueryParameter"},{"$ref":"#/components/parameters/ModelVariantTagQueryParameter"},{"$ref":"#/components/parameters/SourceQueryParameter"},{"$ref":"#/components/parameters/LimitQueryParameter"},{"$ref":"#/components/parameters/OffsetQueryParameter"}],"responses":{"200":{"description":"A paginated list of model configurations.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/ModelConfig"}},"pagination":{"$ref":"#/components/schemas/Pagination"}}}}}},"400":{"$ref":"#/components/responses/BadRequestError"},"401":{"$ref":"#/components/responses/UnauthorizedError"},"403":{"$ref":"#/components/responses/ForbiddenError"}}}}}}
```

## POST /model-configs

> Create a new Model Configuration

```json
{"openapi":"3.0.0","info":{"title":"Ojin REST API","version":"v1.0.0"},"tags":[{"name":"Model Configurations","description":"Operations related to user-defined configurations of model variants for API clients and product integrations."}],"servers":[{"url":"https://api.ojin.ai/v1","description":"Main backend server, version 1."}],"security":[{"APIKeyAuth":[]}],"components":{"securitySchemes":{"APIKeyAuth":{"type":"apiKey","in":"header","name":"X-API-Key","description":"API key for authenticated access to the Ojin REST API."}},"schemas":{"ModelConfigCreationRequest":{"type":"object","description":"Payload for creating a new Model Configuration.","properties":{"title":{"type":"string"},"model_variant_id":{"type":"string"},"model_configurations":{"type":"object","additionalProperties":true,"default":{}}},"required":["title","model_variant_id"]},"ModelConfig":{"type":"object","description":"Represents a specific configuration of a ModelVariant.","properties":{"model_config_id":{"type":"string","format":"uuid","readOnly":true,"description":"Server-generated unique ID."},"organization_id":{"type":"string","readOnly":true,"description":"Owning organization ID (from external IdP)."},"model_id":{"type":"string","readOnly":true,"description":"ID of the parent Model, derived from the Model Variant."},"model_variant_id":{"type":"string","description":"ID of the ModelVariant being configured. NOT NULL."},"created_by":{"type":"string","readOnly":true,"description":"Creator's user ID (from external IdP), or `api_key:<id>` when created via an API key."},"title":{"type":"string","description":"Title for the configuration. NOT NULL, unique per organization_id."},"model_configurations":{"type":"object","additionalProperties":true,"description":"Parameters for the model variant, adhering to its schema. NOT NULL, defaults to '{}'.","default":{}},"created_at":{"type":"string","format":"date-time","readOnly":true,"description":"Timestamp of creation. Defaults to CURRENT_TIMESTAMP."},"updated_at":{"type":"string","format":"date-time","readOnly":true,"description":"Timestamp of last update. Auto-updates."}},"required":["model_config_id","organization_id","model_id","model_variant_id","created_by","title","model_configurations","created_at","updated_at"]},"ErrorResponse":{"type":"object","properties":{"code":{"type":"string","description":"A short, machine-readable error code string."},"message":{"type":"string","description":"A human-readable description of the error."},"details":{"type":"object","additionalProperties":true,"nullable":true,"description":"Optional. Additional structured details about the error."}},"required":["code","message"]}},"responses":{"BadRequestError":{"description":"Invalid request payload or parameters.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"UnauthorizedError":{"description":"Authentication token is missing, invalid, or expired.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"ForbiddenError":{"description":"Authenticated principal does not have permission.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"paths":{"/model-configs":{"post":{"tags":["Model Configurations"],"summary":"Create a new Model Configuration","operationId":"createModelConfig","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ModelConfigCreationRequest"}}}},"responses":{"201":{"description":"Model Configuration created.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ModelConfig"}}}},"400":{"$ref":"#/components/responses/BadRequestError"},"401":{"$ref":"#/components/responses/UnauthorizedError"},"403":{"$ref":"#/components/responses/ForbiddenError"},"404":{"description":"Referenced ModelVariant not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}}}}
```

## GET /model-configs/{model\_config\_id}

> Retrieve a specific Model Configuration

```json
{"openapi":"3.0.0","info":{"title":"Ojin REST API","version":"v1.0.0"},"tags":[{"name":"Model Configurations","description":"Operations related to user-defined configurations of model variants for API clients and product integrations."}],"servers":[{"url":"https://api.ojin.ai/v1","description":"Main backend server, version 1."}],"security":[{"APIKeyAuth":[]}],"components":{"securitySchemes":{"APIKeyAuth":{"type":"apiKey","in":"header","name":"X-API-Key","description":"API key for authenticated access to the Ojin REST API."}},"parameters":{"ModelConfigIdPathParameter":{"name":"model_config_id","in":"path","required":true,"description":"The unique identifier (UUID) of the Model Configuration.","schema":{"type":"string","format":"uuid"}}},"schemas":{"ModelConfig":{"type":"object","description":"Represents a specific configuration of a ModelVariant.","properties":{"model_config_id":{"type":"string","format":"uuid","readOnly":true,"description":"Server-generated unique ID."},"organization_id":{"type":"string","readOnly":true,"description":"Owning organization ID (from external IdP)."},"model_id":{"type":"string","readOnly":true,"description":"ID of the parent Model, derived from the Model Variant."},"model_variant_id":{"type":"string","description":"ID of the ModelVariant being configured. NOT NULL."},"created_by":{"type":"string","readOnly":true,"description":"Creator's user ID (from external IdP), or `api_key:<id>` when created via an API key."},"title":{"type":"string","description":"Title for the configuration. NOT NULL, unique per organization_id."},"model_configurations":{"type":"object","additionalProperties":true,"description":"Parameters for the model variant, adhering to its schema. NOT NULL, defaults to '{}'.","default":{}},"created_at":{"type":"string","format":"date-time","readOnly":true,"description":"Timestamp of creation. Defaults to CURRENT_TIMESTAMP."},"updated_at":{"type":"string","format":"date-time","readOnly":true,"description":"Timestamp of last update. Auto-updates."}},"required":["model_config_id","organization_id","model_id","model_variant_id","created_by","title","model_configurations","created_at","updated_at"]},"ErrorResponse":{"type":"object","properties":{"code":{"type":"string","description":"A short, machine-readable error code string."},"message":{"type":"string","description":"A human-readable description of the error."},"details":{"type":"object","additionalProperties":true,"nullable":true,"description":"Optional. Additional structured details about the error."}},"required":["code","message"]}},"responses":{"UnauthorizedError":{"description":"Authentication token is missing, invalid, or expired.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"ForbiddenError":{"description":"Authenticated principal does not have permission.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"NotFoundError":{"description":"The requested resource was not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"paths":{"/model-configs/{model_config_id}":{"get":{"tags":["Model Configurations"],"summary":"Retrieve a specific Model Configuration","operationId":"getModelConfigById","parameters":[{"$ref":"#/components/parameters/ModelConfigIdPathParameter"}],"responses":{"200":{"description":"Model Configuration details.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ModelConfig"}}}},"401":{"$ref":"#/components/responses/UnauthorizedError"},"403":{"$ref":"#/components/responses/ForbiddenError"},"404":{"$ref":"#/components/responses/NotFoundError"}}}}}}
```

## PUT /model-configs/{model\_config\_id}

> Update an existing Model Configuration

```json
{"openapi":"3.0.0","info":{"title":"Ojin REST API","version":"v1.0.0"},"tags":[{"name":"Model Configurations","description":"Operations related to user-defined configurations of model variants for API clients and product integrations."}],"servers":[{"url":"https://api.ojin.ai/v1","description":"Main backend server, version 1."}],"security":[{"APIKeyAuth":[]}],"components":{"securitySchemes":{"APIKeyAuth":{"type":"apiKey","in":"header","name":"X-API-Key","description":"API key for authenticated access to the Ojin REST API."}},"parameters":{"ModelConfigIdPathParameter":{"name":"model_config_id","in":"path","required":true,"description":"The unique identifier (UUID) of the Model Configuration.","schema":{"type":"string","format":"uuid"}}},"schemas":{"ModelConfigUpdateRequest":{"type":"object","description":"Payload for updating a Model Configuration (title and parameters only).","properties":{"title":{"type":"string"},"model_configurations":{"type":"object","additionalProperties":true}},"required":["title","model_configurations"]},"ModelConfig":{"type":"object","description":"Represents a specific configuration of a ModelVariant.","properties":{"model_config_id":{"type":"string","format":"uuid","readOnly":true,"description":"Server-generated unique ID."},"organization_id":{"type":"string","readOnly":true,"description":"Owning organization ID (from external IdP)."},"model_id":{"type":"string","readOnly":true,"description":"ID of the parent Model, derived from the Model Variant."},"model_variant_id":{"type":"string","description":"ID of the ModelVariant being configured. NOT NULL."},"created_by":{"type":"string","readOnly":true,"description":"Creator's user ID (from external IdP), or `api_key:<id>` when created via an API key."},"title":{"type":"string","description":"Title for the configuration. NOT NULL, unique per organization_id."},"model_configurations":{"type":"object","additionalProperties":true,"description":"Parameters for the model variant, adhering to its schema. NOT NULL, defaults to '{}'.","default":{}},"created_at":{"type":"string","format":"date-time","readOnly":true,"description":"Timestamp of creation. Defaults to CURRENT_TIMESTAMP."},"updated_at":{"type":"string","format":"date-time","readOnly":true,"description":"Timestamp of last update. Auto-updates."}},"required":["model_config_id","organization_id","model_id","model_variant_id","created_by","title","model_configurations","created_at","updated_at"]},"ErrorResponse":{"type":"object","properties":{"code":{"type":"string","description":"A short, machine-readable error code string."},"message":{"type":"string","description":"A human-readable description of the error."},"details":{"type":"object","additionalProperties":true,"nullable":true,"description":"Optional. Additional structured details about the error."}},"required":["code","message"]}},"responses":{"BadRequestError":{"description":"Invalid request payload or parameters.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"UnauthorizedError":{"description":"Authentication token is missing, invalid, or expired.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"ForbiddenError":{"description":"Authenticated principal does not have permission.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"NotFoundError":{"description":"The requested resource was not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"paths":{"/model-configs/{model_config_id}":{"put":{"tags":["Model Configurations"],"summary":"Update an existing Model Configuration","operationId":"updateModelConfig","parameters":[{"$ref":"#/components/parameters/ModelConfigIdPathParameter"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ModelConfigUpdateRequest"}}}},"responses":{"200":{"description":"Model Configuration updated.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ModelConfig"}}}},"400":{"$ref":"#/components/responses/BadRequestError"},"401":{"$ref":"#/components/responses/UnauthorizedError"},"403":{"$ref":"#/components/responses/ForbiddenError"},"404":{"$ref":"#/components/responses/NotFoundError"}}}}}}
```

## DELETE /model-configs/{model\_config\_id}

> Delete a Model Configuration

```json
{"openapi":"3.0.0","info":{"title":"Ojin REST API","version":"v1.0.0"},"tags":[{"name":"Model Configurations","description":"Operations related to user-defined configurations of model variants for API clients and product integrations."}],"servers":[{"url":"https://api.ojin.ai/v1","description":"Main backend server, version 1."}],"security":[{"APIKeyAuth":[]}],"components":{"securitySchemes":{"APIKeyAuth":{"type":"apiKey","in":"header","name":"X-API-Key","description":"API key for authenticated access to the Ojin REST API."}},"parameters":{"ModelConfigIdPathParameter":{"name":"model_config_id","in":"path","required":true,"description":"The unique identifier (UUID) of the Model Configuration.","schema":{"type":"string","format":"uuid"}},"ReferencedByQueryParameter":{"name":"referenced_by","in":"query","required":false,"description":"Delete reference to the agent configuration.","schema":{"type":"string","format":"uuid"}}},"responses":{"NoContentSuccess":{"description":"Operation successful, no content to return."},"UnauthorizedError":{"description":"Authentication token is missing, invalid, or expired.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"ForbiddenError":{"description":"Authenticated principal does not have permission.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"NotFoundError":{"description":"The requested resource was not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"schemas":{"ErrorResponse":{"type":"object","properties":{"code":{"type":"string","description":"A short, machine-readable error code string."},"message":{"type":"string","description":"A human-readable description of the error."},"details":{"type":"object","additionalProperties":true,"nullable":true,"description":"Optional. Additional structured details about the error."}},"required":["code","message"]}}},"paths":{"/model-configs/{model_config_id}":{"delete":{"tags":["Model Configurations"],"summary":"Delete a Model Configuration","operationId":"deleteModelConfig","parameters":[{"$ref":"#/components/parameters/ModelConfigIdPathParameter"},{"$ref":"#/components/parameters/ReferencedByQueryParameter"}],"responses":{"204":{"$ref":"#/components/responses/NoContentSuccess"},"401":{"$ref":"#/components/responses/UnauthorizedError"},"403":{"$ref":"#/components/responses/ForbiddenError"},"404":{"$ref":"#/components/responses/NotFoundError"},"409":{"description":"Conflict - Cannot delete, referenced by AgentConfig.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}}}}
```

## Model Variants

Use model variant endpoints to discover public variants and retrieve the `configuration_schema` that defines the expected shape of `model_configurations`.

## List all Model Variants

> List all Model Variants.\
> Public callers, including API-key-authenticated callers, only receive variants with status="public",\
> ignoring other status filters.

```json
{"openapi":"3.0.0","info":{"title":"Ojin REST API","version":"v1.0.0"},"tags":[{"name":"Model Variants","description":"Operations related to specific variants of models."}],"servers":[{"url":"https://api.ojin.ai/v1","description":"Main backend server, version 1."}],"security":[{"APIKeyAuth":[]}],"components":{"securitySchemes":{"APIKeyAuth":{"type":"apiKey","in":"header","name":"X-API-Key","description":"API key for authenticated access to the Ojin REST API."}},"parameters":{"ModelIdQueryParameter":{"name":"model_id","in":"query","required":false,"description":"Filter model variants by parent model_id.","schema":{"type":"string"}},"StatusQueryParameter":{"name":"status","in":"query","required":false,"description":"Filter by status.","schema":{"type":"string"}},"TagsQueryParameter":{"name":"tags","in":"query","required":false,"description":"Filter model variants by a comma-separated list of tags (AND logic).","style":"form","explode":false,"schema":{"type":"array","items":{"type":"string"}}},"LimitQueryParameter":{"name":"limit","in":"query","required":false,"description":"Number of items to return per page.","schema":{"type":"integer","default":20,"minimum":1,"maximum":100}},"OffsetQueryParameter":{"name":"offset","in":"query","required":false,"description":"Number of items to skip for pagination.","schema":{"type":"integer","default":0,"minimum":0}}},"schemas":{"ModelVariant":{"type":"object","description":"Represents a specific variant of a Model.","properties":{"model_variant_id":{"type":"string","description":"Client-provided unique identifier (e.g., \"ojin/oris-v1/standard\")."},"model_id":{"type":"string","description":"Identifier of the parent Model. NOT NULL."},"title":{"type":"string","description":"Display name for the variant. NOT NULL, unique per model_id."},"description":{"type":"object","additionalProperties":{"type":"string"},"description":"UI display texts. NOT NULL, defaults to '{}'."},"preview_media_url":{"type":"string","format":"url","nullable":true,"description":"URL for a preview media."},"configuration_schema":{"type":"object","description":"JSON schema for ModelConfig.model_configurations. NOT NULL, defaults to '{}'.","additionalProperties":true},"tags":{"type":"array","items":{"type":"string"},"description":"List of descriptive tags. NOT NULL, defaults to '[]'."},"status":{"type":"string","description":"Status (e.g., 'available', 'deprecated'). NOT NULL. List managed in code."},"created_at":{"type":"string","format":"date-time","description":"Timestamp of creation. Server-generated. Defaults to CURRENT_TIMESTAMP.","readOnly":true},"updated_at":{"type":"string","format":"date-time","description":"Timestamp of last update. Server-generated. Auto-updates.","readOnly":true}},"required":["model_variant_id","model_id","title","description","configuration_schema","tags","status","created_at","updated_at"]},"Pagination":{"type":"object","properties":{"limit":{"type":"integer","description":"The number of items returned in the current page."},"offset":{"type":"integer","description":"The number of items skipped before starting the current page."},"total_items":{"type":"integer","description":"The total number of items available that match the query."}},"required":["limit","offset","total_items"]},"ErrorResponse":{"type":"object","properties":{"code":{"type":"string","description":"A short, machine-readable error code string."},"message":{"type":"string","description":"A human-readable description of the error."},"details":{"type":"object","additionalProperties":true,"nullable":true,"description":"Optional. Additional structured details about the error."}},"required":["code","message"]}},"responses":{"BadRequestError":{"description":"Invalid request payload or parameters.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"UnauthorizedError":{"description":"Authentication token is missing, invalid, or expired.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"ForbiddenError":{"description":"Authenticated principal does not have permission.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"paths":{"/model-variants":{"get":{"tags":["Model Variants"],"summary":"List all Model Variants","description":"List all Model Variants.\nPublic callers, including API-key-authenticated callers, only receive variants with status=\"public\",\nignoring other status filters.","operationId":"listModelVariants","parameters":[{"$ref":"#/components/parameters/ModelIdQueryParameter"},{"$ref":"#/components/parameters/StatusQueryParameter"},{"$ref":"#/components/parameters/TagsQueryParameter"},{"$ref":"#/components/parameters/LimitQueryParameter"},{"$ref":"#/components/parameters/OffsetQueryParameter"}],"responses":{"200":{"description":"A paginated list of model variants.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/ModelVariant"}},"pagination":{"$ref":"#/components/schemas/Pagination"}}}}}},"400":{"$ref":"#/components/responses/BadRequestError"},"401":{"$ref":"#/components/responses/UnauthorizedError"},"403":{"$ref":"#/components/responses/ForbiddenError"}}}}}}
```

## Retrieve a specific Model Variant

> Retrieve a specific Model Variant by ID.\
> Public callers, including API-key-authenticated callers, can retrieve the variant only if status="public".\
> Otherwise returns 404.

```json
{"openapi":"3.0.0","info":{"title":"Ojin REST API","version":"v1.0.0"},"tags":[{"name":"Model Variants","description":"Operations related to specific variants of models."}],"servers":[{"url":"https://api.ojin.ai/v1","description":"Main backend server, version 1."}],"security":[{"APIKeyAuth":[]}],"components":{"securitySchemes":{"APIKeyAuth":{"type":"apiKey","in":"header","name":"X-API-Key","description":"API key for authenticated access to the Ojin REST API."}},"parameters":{"ModelVariantIdPathParameter":{"name":"model_variant_id","in":"path","required":true,"description":"The unique identifier of the model variant.","schema":{"type":"string"}}},"schemas":{"ModelVariant":{"type":"object","description":"Represents a specific variant of a Model.","properties":{"model_variant_id":{"type":"string","description":"Client-provided unique identifier (e.g., \"ojin/oris-v1/standard\")."},"model_id":{"type":"string","description":"Identifier of the parent Model. NOT NULL."},"title":{"type":"string","description":"Display name for the variant. NOT NULL, unique per model_id."},"description":{"type":"object","additionalProperties":{"type":"string"},"description":"UI display texts. NOT NULL, defaults to '{}'."},"preview_media_url":{"type":"string","format":"url","nullable":true,"description":"URL for a preview media."},"configuration_schema":{"type":"object","description":"JSON schema for ModelConfig.model_configurations. NOT NULL, defaults to '{}'.","additionalProperties":true},"tags":{"type":"array","items":{"type":"string"},"description":"List of descriptive tags. NOT NULL, defaults to '[]'."},"status":{"type":"string","description":"Status (e.g., 'available', 'deprecated'). NOT NULL. List managed in code."},"created_at":{"type":"string","format":"date-time","description":"Timestamp of creation. Server-generated. Defaults to CURRENT_TIMESTAMP.","readOnly":true},"updated_at":{"type":"string","format":"date-time","description":"Timestamp of last update. Server-generated. Auto-updates.","readOnly":true}},"required":["model_variant_id","model_id","title","description","configuration_schema","tags","status","created_at","updated_at"]},"ErrorResponse":{"type":"object","properties":{"code":{"type":"string","description":"A short, machine-readable error code string."},"message":{"type":"string","description":"A human-readable description of the error."},"details":{"type":"object","additionalProperties":true,"nullable":true,"description":"Optional. Additional structured details about the error."}},"required":["code","message"]}},"responses":{"UnauthorizedError":{"description":"Authentication token is missing, invalid, or expired.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"ForbiddenError":{"description":"Authenticated principal does not have permission.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"NotFoundError":{"description":"The requested resource was not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"paths":{"/model-variants/{model_variant_id}":{"get":{"tags":["Model Variants"],"summary":"Retrieve a specific Model Variant","description":"Retrieve a specific Model Variant by ID.\nPublic callers, including API-key-authenticated callers, can retrieve the variant only if status=\"public\".\nOtherwise returns 404.","operationId":"getModelVariantById","parameters":[{"$ref":"#/components/parameters/ModelVariantIdPathParameter"}],"responses":{"200":{"description":"Model Variant details.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ModelVariant"}}}},"401":{"$ref":"#/components/responses/UnauthorizedError"},"403":{"$ref":"#/components/responses/ForbiddenError"},"404":{"$ref":"#/components/responses/NotFoundError"}}}}}}
```

## Assets

Use asset endpoints to upload, register, retrieve, and delete media used by your Ojin integrations.

## Initiate a Multipart Asset Upload

> Starts the multipart asset upload process by creating a multipart upload session in S3.\
> The Core API generates a unique \`asset\_id\` and receives an \`upload\_id\` from S3.\
> Both IDs must be used in subsequent part signing and finalization requests.\
> No Asset record is created in the database at this stage.

```json
{"openapi":"3.0.0","info":{"title":"Ojin REST API","version":"v1.0.0"},"tags":[{"name":"Assets","description":"Operations related to managing digital assets (images, videos, etc.)."}],"servers":[{"url":"https://api.ojin.ai/v1","description":"Main backend server, version 1."}],"security":[{"APIKeyAuth":[]}],"components":{"securitySchemes":{"APIKeyAuth":{"type":"apiKey","in":"header","name":"X-API-Key","description":"API key for authenticated access to the Ojin REST API."}},"schemas":{"AssetInitiateUploadRequest":{"type":"object","description":"Payload to initiate a multipart asset upload.","properties":{"name":{"type":"string","description":"Original filename of the asset."},"category":{"type":"string","description":"Category for the asset (e.g., 'video', 'image')."},"content_type":{"type":"string","nullable":true,"description":"Client-declared MIME type of the file."},"size_bytes":{"type":"integer","format":"int64","nullable":true,"description":"Client-declared file size in bytes."}},"required":["name","category"]},"AssetInitiateUploadResponse":{"type":"object","description":"Response from initiating a multipart asset upload.","properties":{"asset_id":{"type":"string","format":"uuid","description":"A unique ID generated by Core API for this asset transaction."},"upload_id":{"type":"string","description":"The multipart upload ID from S3, used to identify this multipart upload session."},"s3_key":{"type":"string","description":"The S3 key for this asset."}},"required":["asset_id","upload_id"]},"ErrorResponse":{"type":"object","properties":{"code":{"type":"string","description":"A short, machine-readable error code string."},"message":{"type":"string","description":"A human-readable description of the error."},"details":{"type":"object","additionalProperties":true,"nullable":true,"description":"Optional. Additional structured details about the error."}},"required":["code","message"]}},"responses":{"BadRequestError":{"description":"Invalid request payload or parameters.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"UnauthorizedError":{"description":"Authentication token is missing, invalid, or expired.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"ForbiddenError":{"description":"Authenticated principal does not have permission.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"paths":{"/assets/initiate-upload":{"post":{"tags":["Assets"],"summary":"Initiate a Multipart Asset Upload","description":"Starts the multipart asset upload process by creating a multipart upload session in S3.\nThe Core API generates a unique `asset_id` and receives an `upload_id` from S3.\nBoth IDs must be used in subsequent part signing and finalization requests.\nNo Asset record is created in the database at this stage.","operationId":"initiateAssetUpload","requestBody":{"description":"Initial metadata for the asset to be uploaded.","required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AssetInitiateUploadRequest"}}}},"responses":{"200":{"description":"Multipart upload initiated successfully. Returns asset_id and upload_id.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AssetInitiateUploadResponse"}}}},"400":{"$ref":"#/components/responses/BadRequestError"},"401":{"$ref":"#/components/responses/UnauthorizedError"},"403":{"$ref":"#/components/responses/ForbiddenError"}}}}}}
```

## Get Pre-signed URL for Upload Part

> Generates a pre-signed URL for uploading a specific part of a multipart upload.\
> This endpoint will be called multiple times, once for each part of the file.\
> Parts must be numbered sequentially starting from 1, and each part (except the last)\
> must be at least 5MB in size (S3 requirement).

```json
{"openapi":"3.0.0","info":{"title":"Ojin REST API","version":"v1.0.0"},"tags":[{"name":"Assets","description":"Operations related to managing digital assets (images, videos, etc.)."}],"servers":[{"url":"https://api.ojin.ai/v1","description":"Main backend server, version 1."}],"security":[{"APIKeyAuth":[]}],"components":{"securitySchemes":{"APIKeyAuth":{"type":"apiKey","in":"header","name":"X-API-Key","description":"API key for authenticated access to the Ojin REST API."}},"schemas":{"AssetSignPartRequest":{"type":"object","description":"Payload to get a pre-signed URL for uploading a specific part of a multipart upload.","properties":{"s3_key":{"type":"string","description":"The S3 key for this asset."},"asset_id":{"type":"string","format":"uuid","description":"The asset ID from the initiate upload response."},"upload_id":{"type":"string","description":"The multipart upload ID from the initiate upload response."},"part_number":{"type":"integer","minimum":1,"maximum":10000,"description":"The part number for this upload part (1-10000)."}},"required":["asset_id","upload_id","part_number"]},"AssetSignPartResponse":{"type":"object","description":"Response containing the pre-signed URL for uploading a specific part.","properties":{"upload_url":{"type":"string","format":"url","description":"The pre-signed S3 URL to PUT this specific part to."},"part_number":{"type":"integer","description":"The part number this URL is for."}},"required":["upload_url","part_number"]},"ErrorResponse":{"type":"object","properties":{"code":{"type":"string","description":"A short, machine-readable error code string."},"message":{"type":"string","description":"A human-readable description of the error."},"details":{"type":"object","additionalProperties":true,"nullable":true,"description":"Optional. Additional structured details about the error."}},"required":["code","message"]}},"responses":{"BadRequestError":{"description":"Invalid request payload or parameters.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"UnauthorizedError":{"description":"Authentication token is missing, invalid, or expired.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"ForbiddenError":{"description":"Authenticated principal does not have permission.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"paths":{"/assets/sign-part":{"post":{"tags":["Assets"],"summary":"Get Pre-signed URL for Upload Part","description":"Generates a pre-signed URL for uploading a specific part of a multipart upload.\nThis endpoint will be called multiple times, once for each part of the file.\nParts must be numbered sequentially starting from 1, and each part (except the last)\nmust be at least 5MB in size (S3 requirement).","operationId":"signAssetUploadPart","requestBody":{"description":"Details for the part to be signed.","required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AssetSignPartRequest"}}}},"responses":{"200":{"description":"Pre-signed URL generated successfully for the specified part.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AssetSignPartResponse"}}}},"400":{"$ref":"#/components/responses/BadRequestError"},"401":{"$ref":"#/components/responses/UnauthorizedError"},"403":{"$ref":"#/components/responses/ForbiddenError"},"404":{"description":"Multipart upload session not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}}}}
```

## Finalize Multipart Upload and Create Asset Record

> Completes a multipart upload by combining all uploaded parts and creates the Asset\
> metadata record in the Core API. The client must provide all part numbers and their\
> corresponding ETags from the S3 upload responses. The Core API will complete the\
> multipart upload in S3 and verify the final object before creating the database record.

```json
{"openapi":"3.0.0","info":{"title":"Ojin REST API","version":"v1.0.0"},"tags":[{"name":"Assets","description":"Operations related to managing digital assets (images, videos, etc.)."}],"servers":[{"url":"https://api.ojin.ai/v1","description":"Main backend server, version 1."}],"security":[{"APIKeyAuth":[]}],"components":{"securitySchemes":{"APIKeyAuth":{"type":"apiKey","in":"header","name":"X-API-Key","description":"API key for authenticated access to the Ojin REST API."}},"schemas":{"AssetFinalizationRequest":{"type":"object","description":"Payload to finalize a multipart asset upload and create the asset metadata record in DB.","properties":{"asset_id":{"type":"string","format":"uuid","description":"The unique ID received from the 'initiate-upload' step."},"upload_id":{"type":"string","description":"The multipart upload ID from the 'initiate-upload' step."},"name":{"type":"string","description":"The original filename (must be consistent with initiate request)."},"category":{"type":"string","description":"The asset category (must be consistent with initiate request)."},"content_type":{"type":"string","description":"Final confirmed MIME type of the asset."},"size_bytes":{"type":"integer","format":"int64","description":"Final confirmed size of the asset in bytes."},"parts":{"type":"array","items":{"$ref":"#/components/schemas/AssetUploadPart"},"description":"List of all uploaded parts with their ETags, in order.","minItems":1}},"required":["asset_id","upload_id","name","category","content_type","size_bytes","parts"]},"AssetUploadPart":{"type":"object","description":"Information about a completed upload part.","properties":{"part_number":{"type":"integer","minimum":1,"maximum":10000,"description":"The part number that was uploaded."},"etag":{"type":"string","description":"The ETag returned by S3 after successfully uploading this part."}},"required":["part_number","etag"]},"Asset":{"type":"object","description":"Represents a digital asset managed by the Core API.","properties":{"asset_id":{"type":"string","format":"uuid","description":"Unique identifier for the Asset, generated during upload initiation.","readOnly":true},"organization_id":{"type":"string","description":"ID of the Organisation (from external IdP) that owns this Asset. Server-set.","readOnly":true},"created_by":{"type":"string","description":"User ID of the creator (from external IdP), or `api_key:<id>` when created via an API key. Server-set.","readOnly":true},"name":{"type":"string","description":"The original file name of the Asset. NOT NULL."},"category":{"type":"string","description":"Primary category of the Asset (e.g., 'video', 'image', 'weight'). NOT NULL. List of values managed in code."},"content_type":{"type":"string","description":"The MIME type of the asset. NOT NULL."},"size_bytes":{"type":"integer","format":"int64","description":"The size of the asset in bytes. NOT NULL."},"etag":{"type":"string","description":"The ETag of the S3 object, used for integrity checking. NOT NULL."},"created_at":{"type":"string","format":"date-time","description":"Timestamp of when this Asset record was created (finalized). Server-generated. Defaults to CURRENT_TIMESTAMP.","readOnly":true},"updated_at":{"type":"string","format":"date-time","description":"Timestamp of the last update to this Asset record. Server-generated. Auto-updates on modification.","readOnly":true},"asset_url":{"type":"string","format":"uri","readOnly":true,"description":"A temporary, pre-signed URL to download the asset's content. This URL will expire."}},"required":["asset_id","organization_id","created_by","name","category","content_type","size_bytes","etag","created_at","updated_at"]},"ErrorResponse":{"type":"object","properties":{"code":{"type":"string","description":"A short, machine-readable error code string."},"message":{"type":"string","description":"A human-readable description of the error."},"details":{"type":"object","additionalProperties":true,"nullable":true,"description":"Optional. Additional structured details about the error."}},"required":["code","message"]}},"responses":{"BadRequestError":{"description":"Invalid request payload or parameters.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"UnauthorizedError":{"description":"Authentication token is missing, invalid, or expired.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"ForbiddenError":{"description":"Authenticated principal does not have permission.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"ConflictError":{"description":"Conflict with the current state of the resource.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"paths":{"/assets":{"post":{"tags":["Assets"],"summary":"Finalize Multipart Upload and Create Asset Record","description":"Completes a multipart upload by combining all uploaded parts and creates the Asset\nmetadata record in the Core API. The client must provide all part numbers and their\ncorresponding ETags from the S3 upload responses. The Core API will complete the\nmultipart upload in S3 and verify the final object before creating the database record.","operationId":"createAssetRecord","requestBody":{"description":"Finalization details for the multipart asset upload.","required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AssetFinalizationRequest"}}}},"responses":{"201":{"description":"Multipart upload completed successfully and Asset record created in DB.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Asset"}}}},"400":{"$ref":"#/components/responses/BadRequestError"},"401":{"$ref":"#/components/responses/UnauthorizedError"},"403":{"$ref":"#/components/responses/ForbiddenError"},"404":{"description":"Multipart upload session not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"409":{"$ref":"#/components/responses/ConflictError"}}}}}}
```

## List Assets

> Retrieves a list of Asset metadata. By default returns only organization-owned assets. Use source='template' to retrieve template assets instead. Supports pagination and filtering by category and name.

```json
{"openapi":"3.0.0","info":{"title":"Ojin REST API","version":"v1.0.0"},"tags":[{"name":"Assets","description":"Operations related to managing digital assets (images, videos, etc.)."}],"servers":[{"url":"https://api.ojin.ai/v1","description":"Main backend server, version 1."}],"security":[{"APIKeyAuth":[]}],"components":{"securitySchemes":{"APIKeyAuth":{"type":"apiKey","in":"header","name":"X-API-Key","description":"API key for authenticated access to the Ojin REST API."}},"parameters":{"SourceQueryParameter":{"name":"source","in":"query","required":false,"description":"Filter items by ownership source. Use 'org' for items owned by the user's organization, 'template' for items from the template organization. Defaults to 'org'.","schema":{"type":"string","enum":["org","template"],"default":"org"}},"AssetCategoryQueryParameter":{"name":"category","in":"query","required":false,"description":"Filter assets by category.","schema":{"type":"string"}},"AssetNameQueryParameter":{"name":"name","in":"query","required":false,"description":"Filter assets by name (e.g., for partial match - specific behavior TBD by implementation).","schema":{"type":"string"}},"LimitQueryParameter":{"name":"limit","in":"query","required":false,"description":"Number of items to return per page.","schema":{"type":"integer","default":20,"minimum":1,"maximum":100}},"OffsetQueryParameter":{"name":"offset","in":"query","required":false,"description":"Number of items to skip for pagination.","schema":{"type":"integer","default":0,"minimum":0}}},"schemas":{"Asset":{"type":"object","description":"Represents a digital asset managed by the Core API.","properties":{"asset_id":{"type":"string","format":"uuid","description":"Unique identifier for the Asset, generated during upload initiation.","readOnly":true},"organization_id":{"type":"string","description":"ID of the Organisation (from external IdP) that owns this Asset. Server-set.","readOnly":true},"created_by":{"type":"string","description":"User ID of the creator (from external IdP), or `api_key:<id>` when created via an API key. Server-set.","readOnly":true},"name":{"type":"string","description":"The original file name of the Asset. NOT NULL."},"category":{"type":"string","description":"Primary category of the Asset (e.g., 'video', 'image', 'weight'). NOT NULL. List of values managed in code."},"content_type":{"type":"string","description":"The MIME type of the asset. NOT NULL."},"size_bytes":{"type":"integer","format":"int64","description":"The size of the asset in bytes. NOT NULL."},"etag":{"type":"string","description":"The ETag of the S3 object, used for integrity checking. NOT NULL."},"created_at":{"type":"string","format":"date-time","description":"Timestamp of when this Asset record was created (finalized). Server-generated. Defaults to CURRENT_TIMESTAMP.","readOnly":true},"updated_at":{"type":"string","format":"date-time","description":"Timestamp of the last update to this Asset record. Server-generated. Auto-updates on modification.","readOnly":true},"asset_url":{"type":"string","format":"uri","readOnly":true,"description":"A temporary, pre-signed URL to download the asset's content. This URL will expire."}},"required":["asset_id","organization_id","created_by","name","category","content_type","size_bytes","etag","created_at","updated_at"]},"Pagination":{"type":"object","properties":{"limit":{"type":"integer","description":"The number of items returned in the current page."},"offset":{"type":"integer","description":"The number of items skipped before starting the current page."},"total_items":{"type":"integer","description":"The total number of items available that match the query."}},"required":["limit","offset","total_items"]},"ErrorResponse":{"type":"object","properties":{"code":{"type":"string","description":"A short, machine-readable error code string."},"message":{"type":"string","description":"A human-readable description of the error."},"details":{"type":"object","additionalProperties":true,"nullable":true,"description":"Optional. Additional structured details about the error."}},"required":["code","message"]}},"responses":{"BadRequestError":{"description":"Invalid request payload or parameters.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"UnauthorizedError":{"description":"Authentication token is missing, invalid, or expired.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"ForbiddenError":{"description":"Authenticated principal does not have permission.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"paths":{"/assets":{"get":{"tags":["Assets"],"summary":"List Assets","description":"Retrieves a list of Asset metadata. By default returns only organization-owned assets. Use source='template' to retrieve template assets instead. Supports pagination and filtering by category and name.","operationId":"listAssets","parameters":[{"$ref":"#/components/parameters/SourceQueryParameter"},{"$ref":"#/components/parameters/AssetCategoryQueryParameter"},{"$ref":"#/components/parameters/AssetNameQueryParameter"},{"$ref":"#/components/parameters/LimitQueryParameter"},{"$ref":"#/components/parameters/OffsetQueryParameter"}],"responses":{"200":{"description":"A paginated list of Asset metadata.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Asset"}},"pagination":{"$ref":"#/components/schemas/Pagination"}}}}}},"400":{"$ref":"#/components/responses/BadRequestError"},"401":{"$ref":"#/components/responses/UnauthorizedError"},"403":{"$ref":"#/components/responses/ForbiddenError"}}}}}}
```

## Retrieve Asset Metadata and Download URL

> Retrieves metadata for a specific Asset, including a pre-signed URL for downloading the content. The user can access an asset if it belongs to their organization or to the shared template organization.

```json
{"openapi":"3.0.0","info":{"title":"Ojin REST API","version":"v1.0.0"},"tags":[{"name":"Assets","description":"Operations related to managing digital assets (images, videos, etc.)."}],"servers":[{"url":"https://api.ojin.ai/v1","description":"Main backend server, version 1."}],"security":[{"APIKeyAuth":[]}],"components":{"securitySchemes":{"APIKeyAuth":{"type":"apiKey","in":"header","name":"X-API-Key","description":"API key for authenticated access to the Ojin REST API."}},"parameters":{"AssetIdPathParameter":{"name":"asset_id","in":"path","required":true,"description":"The unique identifier (UUID) of the Asset.","schema":{"type":"string","format":"uuid"}}},"schemas":{"Asset":{"type":"object","description":"Represents a digital asset managed by the Core API.","properties":{"asset_id":{"type":"string","format":"uuid","description":"Unique identifier for the Asset, generated during upload initiation.","readOnly":true},"organization_id":{"type":"string","description":"ID of the Organisation (from external IdP) that owns this Asset. Server-set.","readOnly":true},"created_by":{"type":"string","description":"User ID of the creator (from external IdP), or `api_key:<id>` when created via an API key. Server-set.","readOnly":true},"name":{"type":"string","description":"The original file name of the Asset. NOT NULL."},"category":{"type":"string","description":"Primary category of the Asset (e.g., 'video', 'image', 'weight'). NOT NULL. List of values managed in code."},"content_type":{"type":"string","description":"The MIME type of the asset. NOT NULL."},"size_bytes":{"type":"integer","format":"int64","description":"The size of the asset in bytes. NOT NULL."},"etag":{"type":"string","description":"The ETag of the S3 object, used for integrity checking. NOT NULL."},"created_at":{"type":"string","format":"date-time","description":"Timestamp of when this Asset record was created (finalized). Server-generated. Defaults to CURRENT_TIMESTAMP.","readOnly":true},"updated_at":{"type":"string","format":"date-time","description":"Timestamp of the last update to this Asset record. Server-generated. Auto-updates on modification.","readOnly":true},"asset_url":{"type":"string","format":"uri","readOnly":true,"description":"A temporary, pre-signed URL to download the asset's content. This URL will expire."}},"required":["asset_id","organization_id","created_by","name","category","content_type","size_bytes","etag","created_at","updated_at"]},"ErrorResponse":{"type":"object","properties":{"code":{"type":"string","description":"A short, machine-readable error code string."},"message":{"type":"string","description":"A human-readable description of the error."},"details":{"type":"object","additionalProperties":true,"nullable":true,"description":"Optional. Additional structured details about the error."}},"required":["code","message"]}},"responses":{"UnauthorizedError":{"description":"Authentication token is missing, invalid, or expired.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"ForbiddenError":{"description":"Authenticated principal does not have permission.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"NotFoundError":{"description":"The requested resource was not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"paths":{"/assets/{asset_id}/download":{"get":{"tags":["Assets"],"summary":"Retrieve Asset Metadata and Download URL","description":"Retrieves metadata for a specific Asset, including a pre-signed URL for downloading the content. The user can access an asset if it belongs to their organization or to the shared template organization.","operationId":"getAssetDownloadById","parameters":[{"$ref":"#/components/parameters/AssetIdPathParameter"}],"responses":{"200":{"description":"Successfully retrieved Asset metadata, including a download URL.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/Asset"},{"type":"object","properties":{"asset_url":{"type":"string","format":"uri","readOnly":true,"description":"A temporary, pre-signed URL to download the asset's content. This URL will expire."}}}]}}}},"401":{"$ref":"#/components/responses/UnauthorizedError"},"403":{"$ref":"#/components/responses/ForbiddenError"},"404":{"$ref":"#/components/responses/NotFoundError"}}}}}}
```

## Retrieve Asset Metadata

> Retrieves metadata for a specific Asset, including a temporary pre-signed \`asset\_url\` for download. The user can access an asset if it belongs to their organization or to the shared template organization.

```json
{"openapi":"3.0.0","info":{"title":"Ojin REST API","version":"v1.0.0"},"tags":[{"name":"Assets","description":"Operations related to managing digital assets (images, videos, etc.)."}],"servers":[{"url":"https://api.ojin.ai/v1","description":"Main backend server, version 1."}],"security":[{"APIKeyAuth":[]}],"components":{"securitySchemes":{"APIKeyAuth":{"type":"apiKey","in":"header","name":"X-API-Key","description":"API key for authenticated access to the Ojin REST API."}},"parameters":{"AssetIdPathParameter":{"name":"asset_id","in":"path","required":true,"description":"The unique identifier (UUID) of the Asset.","schema":{"type":"string","format":"uuid"}}},"schemas":{"Asset":{"type":"object","description":"Represents a digital asset managed by the Core API.","properties":{"asset_id":{"type":"string","format":"uuid","description":"Unique identifier for the Asset, generated during upload initiation.","readOnly":true},"organization_id":{"type":"string","description":"ID of the Organisation (from external IdP) that owns this Asset. Server-set.","readOnly":true},"created_by":{"type":"string","description":"User ID of the creator (from external IdP), or `api_key:<id>` when created via an API key. Server-set.","readOnly":true},"name":{"type":"string","description":"The original file name of the Asset. NOT NULL."},"category":{"type":"string","description":"Primary category of the Asset (e.g., 'video', 'image', 'weight'). NOT NULL. List of values managed in code."},"content_type":{"type":"string","description":"The MIME type of the asset. NOT NULL."},"size_bytes":{"type":"integer","format":"int64","description":"The size of the asset in bytes. NOT NULL."},"etag":{"type":"string","description":"The ETag of the S3 object, used for integrity checking. NOT NULL."},"created_at":{"type":"string","format":"date-time","description":"Timestamp of when this Asset record was created (finalized). Server-generated. Defaults to CURRENT_TIMESTAMP.","readOnly":true},"updated_at":{"type":"string","format":"date-time","description":"Timestamp of the last update to this Asset record. Server-generated. Auto-updates on modification.","readOnly":true},"asset_url":{"type":"string","format":"uri","readOnly":true,"description":"A temporary, pre-signed URL to download the asset's content. This URL will expire."}},"required":["asset_id","organization_id","created_by","name","category","content_type","size_bytes","etag","created_at","updated_at"]},"ErrorResponse":{"type":"object","properties":{"code":{"type":"string","description":"A short, machine-readable error code string."},"message":{"type":"string","description":"A human-readable description of the error."},"details":{"type":"object","additionalProperties":true,"nullable":true,"description":"Optional. Additional structured details about the error."}},"required":["code","message"]}},"responses":{"UnauthorizedError":{"description":"Authentication token is missing, invalid, or expired.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"ForbiddenError":{"description":"Authenticated principal does not have permission.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"NotFoundError":{"description":"The requested resource was not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"paths":{"/assets/{asset_id}":{"get":{"tags":["Assets"],"summary":"Retrieve Asset Metadata","description":"Retrieves metadata for a specific Asset, including a temporary pre-signed `asset_url` for download. The user can access an asset if it belongs to their organization or to the shared template organization.","operationId":"getAssetById","parameters":[{"$ref":"#/components/parameters/AssetIdPathParameter"}],"responses":{"200":{"description":"Successfully retrieved Asset metadata.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Asset"}}}},"401":{"$ref":"#/components/responses/UnauthorizedError"},"403":{"$ref":"#/components/responses/ForbiddenError"},"404":{"$ref":"#/components/responses/NotFoundError"}}}}}}
```

## Delete an Asset

> Permanently deletes an Asset's metadata from the DB and its corresponding object from S3 (hard delete). This operation is restricted to assets owned by the user's organization and cannot be used on shared template assets.

```json
{"openapi":"3.0.0","info":{"title":"Ojin REST API","version":"v1.0.0"},"tags":[{"name":"Assets","description":"Operations related to managing digital assets (images, videos, etc.)."}],"servers":[{"url":"https://api.ojin.ai/v1","description":"Main backend server, version 1."}],"security":[{"APIKeyAuth":[]}],"components":{"securitySchemes":{"APIKeyAuth":{"type":"apiKey","in":"header","name":"X-API-Key","description":"API key for authenticated access to the Ojin REST API."}},"parameters":{"AssetIdPathParameter":{"name":"asset_id","in":"path","required":true,"description":"The unique identifier (UUID) of the Asset.","schema":{"type":"string","format":"uuid"}}},"responses":{"NoContentSuccess":{"description":"Operation successful, no content to return."},"UnauthorizedError":{"description":"Authentication token is missing, invalid, or expired.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"ForbiddenError":{"description":"Authenticated principal does not have permission.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"NotFoundError":{"description":"The requested resource was not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"schemas":{"ErrorResponse":{"type":"object","properties":{"code":{"type":"string","description":"A short, machine-readable error code string."},"message":{"type":"string","description":"A human-readable description of the error."},"details":{"type":"object","additionalProperties":true,"nullable":true,"description":"Optional. Additional structured details about the error."}},"required":["code","message"]}}},"paths":{"/assets/{asset_id}":{"delete":{"tags":["Assets"],"summary":"Delete an Asset","description":"Permanently deletes an Asset's metadata from the DB and its corresponding object from S3 (hard delete). This operation is restricted to assets owned by the user's organization and cannot be used on shared template assets.","operationId":"deleteAsset","parameters":[{"$ref":"#/components/parameters/AssetIdPathParameter"}],"responses":{"204":{"$ref":"#/components/responses/NoContentSuccess"},"401":{"$ref":"#/components/responses/UnauthorizedError"},"403":{"$ref":"#/components/responses/ForbiddenError"},"404":{"$ref":"#/components/responses/NotFoundError"}}}}}}
```

{% hint style="warning" %}
The idle video generator endpoint still references the deprecated `ojin/oris-1.0` model name. The underlying service uses the current Oris Portrait model. This endpoint path will be updated in a future release.
{% endhint %}

## Generate idle video from image asset

> Triggers background generation of an idle video from a source image asset\
> using the ojin/oris-1.0 idle video generator.\
> \
> The job runs asynchronously and returns a \`job\_id\` for status tracking.\
> \
> On completion, the generated video is saved as a new Asset in the organization.\
> The \`result.asset\_id\` field in the job object will contain the new asset ID.

```json
{"openapi":"3.0.0","info":{"title":"Ojin REST API","version":"v1.0.0"},"tags":[{"name":"Assets","description":"Operations related to managing digital assets (images, videos, etc.)."}],"servers":[{"url":"https://api.ojin.ai/v1","description":"Main backend server, version 1."}],"security":[{"APIKeyAuth":[]}],"components":{"securitySchemes":{"APIKeyAuth":{"type":"apiKey","in":"header","name":"X-API-Key","description":"API key for authenticated access to the Ojin REST API."}},"schemas":{"IdleVideoGenerationRequest":{"type":"object","description":"Request payload to trigger idle video generation.","properties":{"source_asset_id":{"type":"string","format":"uuid","description":"The ID of the source image asset to use for generating the idle video."},"reference_template":{"type":"string","description":"The reference motion template to use (e.g., 'v1', 'v2', 'v3').","enum":["v1","v2","v3"]},"model_variant_id":{"type":"string","maxLength":128,"description":"Model variant the generated idle video is intended for (e.g. 'ojin/oris-portrait'). Used only to label the background job so the Recent Generations list names the right model. Optional; callers without a model context omit it and the job uses a default label."},"target_model_config_id":{"type":"string","format":"uuid","description":"Model config to assign the generated video to as its active idle preview once the job completes. When set, core-api writes model_configurations.preview server-side on completion, so the assignment lands even if the browser closed, navigated away, or refreshed mid-generation. Must be a config owned by the caller's organization. Optional; omit to only create the video without assigning it."}},"required":["source_asset_id"]},"IdleVideoGenerationResponse":{"type":"object","description":"Response from triggering idle video generation.","properties":{"job_id":{"type":"string","format":"uuid","description":"Identifier of the background job created for this generation request."}},"required":["job_id"]},"ErrorResponse":{"type":"object","properties":{"code":{"type":"string","description":"A short, machine-readable error code string."},"message":{"type":"string","description":"A human-readable description of the error."},"details":{"type":"object","additionalProperties":true,"nullable":true,"description":"Optional. Additional structured details about the error."}},"required":["code","message"]}},"responses":{"BadRequestError":{"description":"Invalid request payload or parameters.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"UnauthorizedError":{"description":"Authentication token is missing, invalid, or expired.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"ForbiddenError":{"description":"Authenticated principal does not have permission.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"paths":{"/assets/generate/ojin/oris-1.0-idle-video-generator":{"post":{"tags":["Assets"],"summary":"Generate idle video from image asset","description":"Triggers background generation of an idle video from a source image asset\nusing the ojin/oris-1.0 idle video generator.\n\nThe job runs asynchronously and returns a `job_id` for status tracking.\n\nOn completion, the generated video is saved as a new Asset in the organization.\nThe `result.asset_id` field in the job object will contain the new asset ID.","operationId":"generateIdleVideo","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IdleVideoGenerationRequest"}}}},"responses":{"202":{"description":"Job accepted and queued for processing.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IdleVideoGenerationResponse"}}}},"400":{"$ref":"#/components/responses/BadRequestError"},"401":{"$ref":"#/components/responses/UnauthorizedError"},"403":{"$ref":"#/components/responses/ForbiddenError"},"404":{"description":"Source asset not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}}}}
```

## Agents

Use the agent endpoints to create and manage Human Agents — their configuration, publication status, third-party voice providers, and call history. These manage the persistent agent definition; to start a live session with a published agent, see the [Session API Reference](/apps/overview/api-reference.md).

## GET /agents

> List Agents (org-scoped)

```json
{"openapi":"3.0.0","info":{"title":"Ojin REST API","version":"v1.0.0"},"tags":[{"name":"Agents","description":"Operations related to Agents (Agent Service v1) — configuration, providers, and call history."}],"servers":[{"url":"https://api.ojin.ai/v1","description":"Main backend server, version 1."}],"security":[{"APIKeyAuth":[]}],"components":{"securitySchemes":{"APIKeyAuth":{"type":"apiKey","in":"header","name":"X-API-Key","description":"API key for authenticated access to the Ojin REST API."}},"parameters":{"LimitQueryParameter":{"name":"limit","in":"query","required":false,"description":"Number of items to return per page.","schema":{"type":"integer","default":20,"minimum":1,"maximum":100}},"OffsetQueryParameter":{"name":"offset","in":"query","required":false,"description":"Number of items to skip for pagination.","schema":{"type":"integer","default":0,"minimum":0}}},"schemas":{"AgentBase":{"type":"object","description":"Basic representation of an Agent for list views.","properties":{"agent_id":{"type":"string","format":"uuid","readOnly":true},"organization_id":{"type":"string","format":"uuid","readOnly":true},"created_by":{"type":"string","readOnly":true,"description":"Creator's user ID (from external IdP), or `api_key:<id>` when the resource was created via an API key."},"title":{"type":"string","maxLength":255},"description":{"type":"string","maxLength":1000,"nullable":true},"mode":{"$ref":"#/components/schemas/AgentMode"},"status":{"$ref":"#/components/schemas/AgentStatus"},"face_id":{"type":"string","format":"uuid","nullable":true},"created_at":{"type":"string","format":"date-time","readOnly":true},"updated_at":{"type":"string","format":"date-time","readOnly":true}},"required":["agent_id","organization_id","created_by","title","mode","status","face_id","created_at","updated_at"]},"AgentMode":{"type":"string","enum":["ojin","third_party"],"description":"The operating mode of an agent. Ojin mode uses the built-in pipeline; third_party mode delegates to an external STS provider."},"AgentStatus":{"type":"string","enum":["published","unpublished"],"description":"The publication status of an agent. Published agents are available for connections; unpublished agents are not."},"ErrorResponse":{"type":"object","properties":{"code":{"type":"string","description":"A short, machine-readable error code string."},"message":{"type":"string","description":"A human-readable description of the error."},"details":{"type":"object","additionalProperties":true,"nullable":true,"description":"Optional. Additional structured details about the error."}},"required":["code","message"]}},"responses":{"UnauthorizedError":{"description":"Authentication token is missing, invalid, or expired.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"paths":{"/agents":{"get":{"tags":["Agents"],"summary":"List Agents (org-scoped)","operationId":"listAgents","parameters":[{"$ref":"#/components/parameters/LimitQueryParameter"},{"$ref":"#/components/parameters/OffsetQueryParameter"}],"responses":{"200":{"description":"Paginated list of Agents.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AgentBase"}},"pagination":{"type":"object","properties":{"limit":{"type":"integer"},"offset":{"type":"integer"},"total_items":{"type":"integer"}},"required":["limit","offset","total_items"]}},"required":["data","pagination"]}}}},"401":{"$ref":"#/components/responses/UnauthorizedError"}}}}}}
```

## Create a new Agent

> Creates an Agent, optionally seeded from a preset. On creation the response includes \`auth\_secret\` (one-shot) when \`auth\_enabled\` is true.

```json
{"openapi":"3.0.0","info":{"title":"Ojin REST API","version":"v1.0.0"},"tags":[{"name":"Agents","description":"Operations related to Agents (Agent Service v1) — configuration, providers, and call history."}],"servers":[{"url":"https://api.ojin.ai/v1","description":"Main backend server, version 1."}],"security":[{"APIKeyAuth":[]}],"components":{"securitySchemes":{"APIKeyAuth":{"type":"apiKey","in":"header","name":"X-API-Key","description":"API key for authenticated access to the Ojin REST API."}},"schemas":{"CreateAgentRequest":{"type":"object","description":"Payload for creating a new Agent, optionally from a preset.","properties":{"title":{"type":"string","maxLength":255},"mode":{"$ref":"#/components/schemas/AgentMode"},"preset_id":{"type":"string","format":"uuid","description":"Optional preset to seed the new agent from."}},"required":["title"]},"AgentMode":{"type":"string","enum":["ojin","third_party"],"description":"The operating mode of an agent. Ojin mode uses the built-in pipeline; third_party mode delegates to an external STS provider."},"AgentDetail":{"allOf":[{"$ref":"#/components/schemas/AgentBase"},{"type":"object","description":"Full Agent detail including configuration, STS credentials flags, LLM tool wiring.","properties":{"language":{"$ref":"#/components/schemas/AgentLanguage"},"system_prompt":{"type":"string","maxLength":10000,"nullable":true},"tts_provider":{"type":"string"},"voice_id":{"type":"string","maxLength":255,"nullable":true},"behaviour":{"allOf":[{"$ref":"#/components/schemas/AgentBehaviour"}],"nullable":true},"advanced":{"allOf":[{"$ref":"#/components/schemas/AgentAdvanced"}],"nullable":true},"sts_provider":{"type":"string","maxLength":100,"nullable":true},"sts_config_id":{"type":"string","maxLength":255,"nullable":true},"sts_api_key_set":{"type":"boolean","description":"True when an encrypted STS API key is persisted for this agent. The raw key is never returned."},"provider_config":{"type":"object","additionalProperties":true,"nullable":true},"preview_url":{"type":"string","maxLength":2048,"nullable":true,"description":"A direct URL or a bare asset-ID UUID. The public agent endpoint resolves UUIDs to signed S3 URLs before returning them to clients."},"starts_conversation":{"type":"boolean"},"session_limit_seconds":{"type":"integer","minimum":1,"maximum":86400},"max_concurrency":{"oneOf":[{"type":"integer","enum":[-1]},{"type":"integer","minimum":1,"maximum":100}],"description":"Maximum concurrent sessions. -1 means unbounded (no limit); valid positive values are 1–100."},"auth_enabled":{"type":"boolean"},"auth_secret_set":{"type":"boolean","description":"True when an auth secret is persisted."},"auth_secret":{"type":"string","nullable":true,"description":"Plaintext auth secret. Only returned to the creator immediately after\nagent creation (one-shot). Subsequent reads return null; use auth_secret_set\nto detect whether a secret is configured."},"allowed_hostnames":{"type":"array","nullable":true,"items":{"type":"string","maxLength":255}},"tools":{"type":"array","description":"Per-tool function-calling definitions with delivery + enabled flag.","items":{"$ref":"#/components/schemas/AgentTool"}}},"required":["language","tts_provider","sts_api_key_set","starts_conversation","session_limit_seconds","max_concurrency","auth_enabled","auth_secret_set","tools"]}]},"AgentBase":{"type":"object","description":"Basic representation of an Agent for list views.","properties":{"agent_id":{"type":"string","format":"uuid","readOnly":true},"organization_id":{"type":"string","format":"uuid","readOnly":true},"created_by":{"type":"string","readOnly":true,"description":"Creator's user ID (from external IdP), or `api_key:<id>` when the resource was created via an API key."},"title":{"type":"string","maxLength":255},"description":{"type":"string","maxLength":1000,"nullable":true},"mode":{"$ref":"#/components/schemas/AgentMode"},"status":{"$ref":"#/components/schemas/AgentStatus"},"face_id":{"type":"string","format":"uuid","nullable":true},"created_at":{"type":"string","format":"date-time","readOnly":true},"updated_at":{"type":"string","format":"date-time","readOnly":true}},"required":["agent_id","organization_id","created_by","title","mode","status","face_id","created_at","updated_at"]},"AgentStatus":{"type":"string","enum":["published","unpublished"],"description":"The publication status of an agent. Published agents are available for connections; unpublished agents are not."},"AgentLanguage":{"type":"string","enum":["en","de","fr","es","ar","ja"],"description":"Spoken language code for the agent. Drives Deepgram STT language,\nElevenLabs voice filtering in the dashboard, and an LLM\nsystem-prompt suffix that pins the response language.\n"},"AgentBehaviour":{"type":"object","description":"Conversational behaviour knobs applied to an Agent.","properties":{"greeting":{"type":"string","maxLength":2000,"nullable":true},"greeting_reference":{"type":"string","maxLength":2000,"nullable":true},"greeting_mode":{"type":"string","enum":["verbatim","composed"],"nullable":true},"nudge_message":{"type":"string","maxLength":2000,"nullable":true},"nudge_reference":{"type":"string","maxLength":2000,"nullable":true},"nudge_mode":{"type":"string","enum":["verbatim","composed"],"nullable":true},"farewell_message":{"type":"string","maxLength":2000,"nullable":true},"farewell_reference":{"type":"string","maxLength":2000,"nullable":true},"farewell_mode":{"type":"string","enum":["verbatim","composed"],"nullable":true},"max_conversation_duration_seconds":{"type":"integer","minimum":1,"maximum":86400,"nullable":true},"inactivity_nudge_seconds":{"type":"integer","minimum":1,"maximum":3600,"nullable":true},"allow_interruption":{"type":"boolean","nullable":true}}},"AgentAdvanced":{"type":"object","description":"Advanced configuration for an Agent (video pipeline + LLM tuning).","properties":{"max_video_source_dimension":{"type":"integer","minimum":1,"nullable":true},"llm_max_tokens":{"type":"integer","minimum":64,"maximum":4096,"nullable":true,"description":"Maximum completion tokens the managed (Ojin-mode) LLM may generate per spoken reply. Raise it if answers get cut off mid-sentence; lower it to keep replies short. Defaults to 384 when unset."}}},"AgentTool":{"type":"object","additionalProperties":false,"description":"A function-calling tool exposed by the agent to the LLM, with per-tool delivery and enabled flag.","properties":{"tool_id":{"type":"string","format":"uuid","readOnly":true},"enabled":{"type":"boolean","default":true,"description":"When false, the tool is hidden from the LLM but its delivery config is preserved."},"type":{"type":"string","enum":["function"],"default":"function"},"function":{"$ref":"#/components/schemas/ToolFunction"},"delivery":{"$ref":"#/components/schemas/ToolDelivery"}},"required":["tool_id","enabled","type","function","delivery"]},"ToolFunction":{"type":"object","additionalProperties":false,"description":"OpenAI / Anthropic tool function definition.","properties":{"name":{"type":"string","maxLength":64,"pattern":"^[a-zA-Z0-9_-]+$","description":"Function name. Must match the LLM tool-name regex."},"description":{"type":"string","maxLength":1024,"nullable":true},"parameters":{"type":"object","additionalProperties":true,"nullable":true,"description":"JSON Schema for the function's parameters (opaque pass-through)."}},"required":["name"]},"ToolDelivery":{"oneOf":[{"$ref":"#/components/schemas/ClientToolDelivery"},{"$ref":"#/components/schemas/WebhookToolDelivery"}],"discriminator":{"propertyName":"mode","mapping":{"client":"#/components/schemas/ClientToolDelivery","webhook":"#/components/schemas/WebhookToolDelivery"}}},"ClientToolDelivery":{"type":"object","additionalProperties":false,"description":"Tool calls are delivered to the in-browser widget as an `ojinToolCall` CustomEvent.","properties":{"mode":{"type":"string","enum":["client"]}},"required":["mode"]},"WebhookToolDelivery":{"type":"object","additionalProperties":false,"description":"Tool calls are POSTed to an external webhook URL.","properties":{"mode":{"type":"string","enum":["webhook"]},"url":{"type":"string","format":"uri","maxLength":2048},"wait_for_response":{"type":"boolean","default":false,"description":"When true, the LLM waits for the webhook's response and incorporates it\ninto the conversation. When false, the webhook is fired-and-forgotten and\nthe LLM receives a neutral acknowledgement immediately."}},"required":["mode","url"]},"ErrorResponse":{"type":"object","properties":{"code":{"type":"string","description":"A short, machine-readable error code string."},"message":{"type":"string","description":"A human-readable description of the error."},"details":{"type":"object","additionalProperties":true,"nullable":true,"description":"Optional. Additional structured details about the error."}},"required":["code","message"]}},"responses":{"BadRequestError":{"description":"Invalid request payload or parameters.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"UnauthorizedError":{"description":"Authentication token is missing, invalid, or expired.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"paths":{"/agents":{"post":{"tags":["Agents"],"summary":"Create a new Agent","description":"Creates an Agent, optionally seeded from a preset. On creation the response includes `auth_secret` (one-shot) when `auth_enabled` is true.","operationId":"createAgent","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateAgentRequest"}}}},"responses":{"201":{"description":"Agent created.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentDetail"}}}},"400":{"$ref":"#/components/responses/BadRequestError"},"401":{"$ref":"#/components/responses/UnauthorizedError"}}}}}}
```

## GET /agents/{agent\_id}

> Retrieve an Agent

```json
{"openapi":"3.0.0","info":{"title":"Ojin REST API","version":"v1.0.0"},"tags":[{"name":"Agents","description":"Operations related to Agents (Agent Service v1) — configuration, providers, and call history."}],"servers":[{"url":"https://api.ojin.ai/v1","description":"Main backend server, version 1."}],"security":[{"APIKeyAuth":[]}],"components":{"securitySchemes":{"APIKeyAuth":{"type":"apiKey","in":"header","name":"X-API-Key","description":"API key for authenticated access to the Ojin REST API."}},"parameters":{"AgentIdPathParameter":{"name":"agent_id","in":"path","required":true,"description":"The unique identifier (UUID) of the Agent.","schema":{"type":"string","format":"uuid"}}},"schemas":{"AgentDetail":{"allOf":[{"$ref":"#/components/schemas/AgentBase"},{"type":"object","description":"Full Agent detail including configuration, STS credentials flags, LLM tool wiring.","properties":{"language":{"$ref":"#/components/schemas/AgentLanguage"},"system_prompt":{"type":"string","maxLength":10000,"nullable":true},"tts_provider":{"type":"string"},"voice_id":{"type":"string","maxLength":255,"nullable":true},"behaviour":{"allOf":[{"$ref":"#/components/schemas/AgentBehaviour"}],"nullable":true},"advanced":{"allOf":[{"$ref":"#/components/schemas/AgentAdvanced"}],"nullable":true},"sts_provider":{"type":"string","maxLength":100,"nullable":true},"sts_config_id":{"type":"string","maxLength":255,"nullable":true},"sts_api_key_set":{"type":"boolean","description":"True when an encrypted STS API key is persisted for this agent. The raw key is never returned."},"provider_config":{"type":"object","additionalProperties":true,"nullable":true},"preview_url":{"type":"string","maxLength":2048,"nullable":true,"description":"A direct URL or a bare asset-ID UUID. The public agent endpoint resolves UUIDs to signed S3 URLs before returning them to clients."},"starts_conversation":{"type":"boolean"},"session_limit_seconds":{"type":"integer","minimum":1,"maximum":86400},"max_concurrency":{"oneOf":[{"type":"integer","enum":[-1]},{"type":"integer","minimum":1,"maximum":100}],"description":"Maximum concurrent sessions. -1 means unbounded (no limit); valid positive values are 1–100."},"auth_enabled":{"type":"boolean"},"auth_secret_set":{"type":"boolean","description":"True when an auth secret is persisted."},"auth_secret":{"type":"string","nullable":true,"description":"Plaintext auth secret. Only returned to the creator immediately after\nagent creation (one-shot). Subsequent reads return null; use auth_secret_set\nto detect whether a secret is configured."},"allowed_hostnames":{"type":"array","nullable":true,"items":{"type":"string","maxLength":255}},"tools":{"type":"array","description":"Per-tool function-calling definitions with delivery + enabled flag.","items":{"$ref":"#/components/schemas/AgentTool"}}},"required":["language","tts_provider","sts_api_key_set","starts_conversation","session_limit_seconds","max_concurrency","auth_enabled","auth_secret_set","tools"]}]},"AgentBase":{"type":"object","description":"Basic representation of an Agent for list views.","properties":{"agent_id":{"type":"string","format":"uuid","readOnly":true},"organization_id":{"type":"string","format":"uuid","readOnly":true},"created_by":{"type":"string","readOnly":true,"description":"Creator's user ID (from external IdP), or `api_key:<id>` when the resource was created via an API key."},"title":{"type":"string","maxLength":255},"description":{"type":"string","maxLength":1000,"nullable":true},"mode":{"$ref":"#/components/schemas/AgentMode"},"status":{"$ref":"#/components/schemas/AgentStatus"},"face_id":{"type":"string","format":"uuid","nullable":true},"created_at":{"type":"string","format":"date-time","readOnly":true},"updated_at":{"type":"string","format":"date-time","readOnly":true}},"required":["agent_id","organization_id","created_by","title","mode","status","face_id","created_at","updated_at"]},"AgentMode":{"type":"string","enum":["ojin","third_party"],"description":"The operating mode of an agent. Ojin mode uses the built-in pipeline; third_party mode delegates to an external STS provider."},"AgentStatus":{"type":"string","enum":["published","unpublished"],"description":"The publication status of an agent. Published agents are available for connections; unpublished agents are not."},"AgentLanguage":{"type":"string","enum":["en","de","fr","es","ar","ja"],"description":"Spoken language code for the agent. Drives Deepgram STT language,\nElevenLabs voice filtering in the dashboard, and an LLM\nsystem-prompt suffix that pins the response language.\n"},"AgentBehaviour":{"type":"object","description":"Conversational behaviour knobs applied to an Agent.","properties":{"greeting":{"type":"string","maxLength":2000,"nullable":true},"greeting_reference":{"type":"string","maxLength":2000,"nullable":true},"greeting_mode":{"type":"string","enum":["verbatim","composed"],"nullable":true},"nudge_message":{"type":"string","maxLength":2000,"nullable":true},"nudge_reference":{"type":"string","maxLength":2000,"nullable":true},"nudge_mode":{"type":"string","enum":["verbatim","composed"],"nullable":true},"farewell_message":{"type":"string","maxLength":2000,"nullable":true},"farewell_reference":{"type":"string","maxLength":2000,"nullable":true},"farewell_mode":{"type":"string","enum":["verbatim","composed"],"nullable":true},"max_conversation_duration_seconds":{"type":"integer","minimum":1,"maximum":86400,"nullable":true},"inactivity_nudge_seconds":{"type":"integer","minimum":1,"maximum":3600,"nullable":true},"allow_interruption":{"type":"boolean","nullable":true}}},"AgentAdvanced":{"type":"object","description":"Advanced configuration for an Agent (video pipeline + LLM tuning).","properties":{"max_video_source_dimension":{"type":"integer","minimum":1,"nullable":true},"llm_max_tokens":{"type":"integer","minimum":64,"maximum":4096,"nullable":true,"description":"Maximum completion tokens the managed (Ojin-mode) LLM may generate per spoken reply. Raise it if answers get cut off mid-sentence; lower it to keep replies short. Defaults to 384 when unset."}}},"AgentTool":{"type":"object","additionalProperties":false,"description":"A function-calling tool exposed by the agent to the LLM, with per-tool delivery and enabled flag.","properties":{"tool_id":{"type":"string","format":"uuid","readOnly":true},"enabled":{"type":"boolean","default":true,"description":"When false, the tool is hidden from the LLM but its delivery config is preserved."},"type":{"type":"string","enum":["function"],"default":"function"},"function":{"$ref":"#/components/schemas/ToolFunction"},"delivery":{"$ref":"#/components/schemas/ToolDelivery"}},"required":["tool_id","enabled","type","function","delivery"]},"ToolFunction":{"type":"object","additionalProperties":false,"description":"OpenAI / Anthropic tool function definition.","properties":{"name":{"type":"string","maxLength":64,"pattern":"^[a-zA-Z0-9_-]+$","description":"Function name. Must match the LLM tool-name regex."},"description":{"type":"string","maxLength":1024,"nullable":true},"parameters":{"type":"object","additionalProperties":true,"nullable":true,"description":"JSON Schema for the function's parameters (opaque pass-through)."}},"required":["name"]},"ToolDelivery":{"oneOf":[{"$ref":"#/components/schemas/ClientToolDelivery"},{"$ref":"#/components/schemas/WebhookToolDelivery"}],"discriminator":{"propertyName":"mode","mapping":{"client":"#/components/schemas/ClientToolDelivery","webhook":"#/components/schemas/WebhookToolDelivery"}}},"ClientToolDelivery":{"type":"object","additionalProperties":false,"description":"Tool calls are delivered to the in-browser widget as an `ojinToolCall` CustomEvent.","properties":{"mode":{"type":"string","enum":["client"]}},"required":["mode"]},"WebhookToolDelivery":{"type":"object","additionalProperties":false,"description":"Tool calls are POSTed to an external webhook URL.","properties":{"mode":{"type":"string","enum":["webhook"]},"url":{"type":"string","format":"uri","maxLength":2048},"wait_for_response":{"type":"boolean","default":false,"description":"When true, the LLM waits for the webhook's response and incorporates it\ninto the conversation. When false, the webhook is fired-and-forgotten and\nthe LLM receives a neutral acknowledgement immediately."}},"required":["mode","url"]},"ErrorResponse":{"type":"object","properties":{"code":{"type":"string","description":"A short, machine-readable error code string."},"message":{"type":"string","description":"A human-readable description of the error."},"details":{"type":"object","additionalProperties":true,"nullable":true,"description":"Optional. Additional structured details about the error."}},"required":["code","message"]}},"responses":{"UnauthorizedError":{"description":"Authentication token is missing, invalid, or expired.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"NotFoundError":{"description":"The requested resource was not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"paths":{"/agents/{agent_id}":{"get":{"tags":["Agents"],"summary":"Retrieve an Agent","operationId":"getAgent","parameters":[{"$ref":"#/components/parameters/AgentIdPathParameter"}],"responses":{"200":{"description":"Agent detail.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentDetail"}}}},"401":{"$ref":"#/components/responses/UnauthorizedError"},"404":{"$ref":"#/components/responses/NotFoundError"}}}}}}
```

## PATCH /agents/{agent\_id}

> Update an Agent (partial)

```json
{"openapi":"3.0.0","info":{"title":"Ojin REST API","version":"v1.0.0"},"tags":[{"name":"Agents","description":"Operations related to Agents (Agent Service v1) — configuration, providers, and call history."}],"servers":[{"url":"https://api.ojin.ai/v1","description":"Main backend server, version 1."}],"security":[{"APIKeyAuth":[]}],"components":{"securitySchemes":{"APIKeyAuth":{"type":"apiKey","in":"header","name":"X-API-Key","description":"API key for authenticated access to the Ojin REST API."}},"parameters":{"AgentIdPathParameter":{"name":"agent_id","in":"path","required":true,"description":"The unique identifier (UUID) of the Agent.","schema":{"type":"string","format":"uuid"}}},"schemas":{"UpdateAgentRequest":{"type":"object","description":"Payload for updating an Agent (partial update — all fields optional).","properties":{"title":{"type":"string","maxLength":255},"description":{"type":"string","maxLength":1000,"nullable":true},"mode":{"$ref":"#/components/schemas/AgentMode"},"face_id":{"type":"string","format":"uuid","nullable":true},"language":{"$ref":"#/components/schemas/AgentLanguage"},"system_prompt":{"type":"string","maxLength":10000,"nullable":true},"tts_provider":{"type":"string","enum":["elevenlabs","ojin"]},"voice_id":{"type":"string","maxLength":255,"nullable":true},"behaviour":{"$ref":"#/components/schemas/AgentBehaviour"},"advanced":{"$ref":"#/components/schemas/AgentAdvanced"},"sts_provider":{"type":"string","maxLength":100,"nullable":true},"sts_api_key":{"type":"string","maxLength":1000,"nullable":true,"writeOnly":true},"sts_config_id":{"type":"string","maxLength":255,"nullable":true},"provider_config":{"type":"object","additionalProperties":true,"nullable":true},"preview_url":{"type":"string","maxLength":2048,"nullable":true,"description":"A direct URL or a bare asset-ID UUID. The public agent endpoint resolves UUIDs to signed S3 URLs before returning them to clients."},"session_limit_seconds":{"type":"integer","minimum":1,"maximum":86400},"max_concurrency":{"oneOf":[{"type":"integer","enum":[-1]},{"type":"integer","minimum":1,"maximum":100}],"description":"Maximum concurrent sessions. -1 means unbounded (no limit); valid positive values are 1–100."},"starts_conversation":{"type":"boolean"},"auth_enabled":{"type":"boolean"},"allowed_hostnames":{"type":"array","items":{"type":"string","maxLength":255}},"tools":{"type":"array","description":"Replace the agent's tools with this array (full-replace semantics).\nOmit to leave tools unchanged. UPSERTs by `function.name`; tools not\npresent in the array are deleted from the agent.","items":{"$ref":"#/components/schemas/AgentToolInput"}}}},"AgentMode":{"type":"string","enum":["ojin","third_party"],"description":"The operating mode of an agent. Ojin mode uses the built-in pipeline; third_party mode delegates to an external STS provider."},"AgentLanguage":{"type":"string","enum":["en","de","fr","es","ar","ja"],"description":"Spoken language code for the agent. Drives Deepgram STT language,\nElevenLabs voice filtering in the dashboard, and an LLM\nsystem-prompt suffix that pins the response language.\n"},"AgentBehaviour":{"type":"object","description":"Conversational behaviour knobs applied to an Agent.","properties":{"greeting":{"type":"string","maxLength":2000,"nullable":true},"greeting_reference":{"type":"string","maxLength":2000,"nullable":true},"greeting_mode":{"type":"string","enum":["verbatim","composed"],"nullable":true},"nudge_message":{"type":"string","maxLength":2000,"nullable":true},"nudge_reference":{"type":"string","maxLength":2000,"nullable":true},"nudge_mode":{"type":"string","enum":["verbatim","composed"],"nullable":true},"farewell_message":{"type":"string","maxLength":2000,"nullable":true},"farewell_reference":{"type":"string","maxLength":2000,"nullable":true},"farewell_mode":{"type":"string","enum":["verbatim","composed"],"nullable":true},"max_conversation_duration_seconds":{"type":"integer","minimum":1,"maximum":86400,"nullable":true},"inactivity_nudge_seconds":{"type":"integer","minimum":1,"maximum":3600,"nullable":true},"allow_interruption":{"type":"boolean","nullable":true}}},"AgentAdvanced":{"type":"object","description":"Advanced configuration for an Agent (video pipeline + LLM tuning).","properties":{"max_video_source_dimension":{"type":"integer","minimum":1,"nullable":true},"llm_max_tokens":{"type":"integer","minimum":64,"maximum":4096,"nullable":true,"description":"Maximum completion tokens the managed (Ojin-mode) LLM may generate per spoken reply. Raise it if answers get cut off mid-sentence; lower it to keep replies short. Defaults to 384 when unset."}}},"AgentToolInput":{"type":"object","additionalProperties":false,"description":"Input shape for an agent tool. `tool_id` is optional — when present and\nmatched (by `function.name`) the existing row is updated in place; when\nabsent or unmatched a new tool is created. `enabled` defaults to true\nand `type` defaults to \"function\" server-side when omitted.","properties":{"tool_id":{"type":"string","format":"uuid"},"enabled":{"type":"boolean"},"type":{"type":"string","enum":["function"]},"function":{"$ref":"#/components/schemas/ToolFunction"},"delivery":{"$ref":"#/components/schemas/ToolDelivery"}},"required":["function","delivery"]},"ToolFunction":{"type":"object","additionalProperties":false,"description":"OpenAI / Anthropic tool function definition.","properties":{"name":{"type":"string","maxLength":64,"pattern":"^[a-zA-Z0-9_-]+$","description":"Function name. Must match the LLM tool-name regex."},"description":{"type":"string","maxLength":1024,"nullable":true},"parameters":{"type":"object","additionalProperties":true,"nullable":true,"description":"JSON Schema for the function's parameters (opaque pass-through)."}},"required":["name"]},"ToolDelivery":{"oneOf":[{"$ref":"#/components/schemas/ClientToolDelivery"},{"$ref":"#/components/schemas/WebhookToolDelivery"}],"discriminator":{"propertyName":"mode","mapping":{"client":"#/components/schemas/ClientToolDelivery","webhook":"#/components/schemas/WebhookToolDelivery"}}},"ClientToolDelivery":{"type":"object","additionalProperties":false,"description":"Tool calls are delivered to the in-browser widget as an `ojinToolCall` CustomEvent.","properties":{"mode":{"type":"string","enum":["client"]}},"required":["mode"]},"WebhookToolDelivery":{"type":"object","additionalProperties":false,"description":"Tool calls are POSTed to an external webhook URL.","properties":{"mode":{"type":"string","enum":["webhook"]},"url":{"type":"string","format":"uri","maxLength":2048},"wait_for_response":{"type":"boolean","default":false,"description":"When true, the LLM waits for the webhook's response and incorporates it\ninto the conversation. When false, the webhook is fired-and-forgotten and\nthe LLM receives a neutral acknowledgement immediately."}},"required":["mode","url"]},"AgentDetail":{"allOf":[{"$ref":"#/components/schemas/AgentBase"},{"type":"object","description":"Full Agent detail including configuration, STS credentials flags, LLM tool wiring.","properties":{"language":{"$ref":"#/components/schemas/AgentLanguage"},"system_prompt":{"type":"string","maxLength":10000,"nullable":true},"tts_provider":{"type":"string"},"voice_id":{"type":"string","maxLength":255,"nullable":true},"behaviour":{"allOf":[{"$ref":"#/components/schemas/AgentBehaviour"}],"nullable":true},"advanced":{"allOf":[{"$ref":"#/components/schemas/AgentAdvanced"}],"nullable":true},"sts_provider":{"type":"string","maxLength":100,"nullable":true},"sts_config_id":{"type":"string","maxLength":255,"nullable":true},"sts_api_key_set":{"type":"boolean","description":"True when an encrypted STS API key is persisted for this agent. The raw key is never returned."},"provider_config":{"type":"object","additionalProperties":true,"nullable":true},"preview_url":{"type":"string","maxLength":2048,"nullable":true,"description":"A direct URL or a bare asset-ID UUID. The public agent endpoint resolves UUIDs to signed S3 URLs before returning them to clients."},"starts_conversation":{"type":"boolean"},"session_limit_seconds":{"type":"integer","minimum":1,"maximum":86400},"max_concurrency":{"oneOf":[{"type":"integer","enum":[-1]},{"type":"integer","minimum":1,"maximum":100}],"description":"Maximum concurrent sessions. -1 means unbounded (no limit); valid positive values are 1–100."},"auth_enabled":{"type":"boolean"},"auth_secret_set":{"type":"boolean","description":"True when an auth secret is persisted."},"auth_secret":{"type":"string","nullable":true,"description":"Plaintext auth secret. Only returned to the creator immediately after\nagent creation (one-shot). Subsequent reads return null; use auth_secret_set\nto detect whether a secret is configured."},"allowed_hostnames":{"type":"array","nullable":true,"items":{"type":"string","maxLength":255}},"tools":{"type":"array","description":"Per-tool function-calling definitions with delivery + enabled flag.","items":{"$ref":"#/components/schemas/AgentTool"}}},"required":["language","tts_provider","sts_api_key_set","starts_conversation","session_limit_seconds","max_concurrency","auth_enabled","auth_secret_set","tools"]}]},"AgentBase":{"type":"object","description":"Basic representation of an Agent for list views.","properties":{"agent_id":{"type":"string","format":"uuid","readOnly":true},"organization_id":{"type":"string","format":"uuid","readOnly":true},"created_by":{"type":"string","readOnly":true,"description":"Creator's user ID (from external IdP), or `api_key:<id>` when the resource was created via an API key."},"title":{"type":"string","maxLength":255},"description":{"type":"string","maxLength":1000,"nullable":true},"mode":{"$ref":"#/components/schemas/AgentMode"},"status":{"$ref":"#/components/schemas/AgentStatus"},"face_id":{"type":"string","format":"uuid","nullable":true},"created_at":{"type":"string","format":"date-time","readOnly":true},"updated_at":{"type":"string","format":"date-time","readOnly":true}},"required":["agent_id","organization_id","created_by","title","mode","status","face_id","created_at","updated_at"]},"AgentStatus":{"type":"string","enum":["published","unpublished"],"description":"The publication status of an agent. Published agents are available for connections; unpublished agents are not."},"AgentTool":{"type":"object","additionalProperties":false,"description":"A function-calling tool exposed by the agent to the LLM, with per-tool delivery and enabled flag.","properties":{"tool_id":{"type":"string","format":"uuid","readOnly":true},"enabled":{"type":"boolean","default":true,"description":"When false, the tool is hidden from the LLM but its delivery config is preserved."},"type":{"type":"string","enum":["function"],"default":"function"},"function":{"$ref":"#/components/schemas/ToolFunction"},"delivery":{"$ref":"#/components/schemas/ToolDelivery"}},"required":["tool_id","enabled","type","function","delivery"]},"ErrorResponse":{"type":"object","properties":{"code":{"type":"string","description":"A short, machine-readable error code string."},"message":{"type":"string","description":"A human-readable description of the error."},"details":{"type":"object","additionalProperties":true,"nullable":true,"description":"Optional. Additional structured details about the error."}},"required":["code","message"]}},"responses":{"BadRequestError":{"description":"Invalid request payload or parameters.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"UnauthorizedError":{"description":"Authentication token is missing, invalid, or expired.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"NotFoundError":{"description":"The requested resource was not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"paths":{"/agents/{agent_id}":{"patch":{"tags":["Agents"],"summary":"Update an Agent (partial)","operationId":"updateAgent","parameters":[{"$ref":"#/components/parameters/AgentIdPathParameter"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateAgentRequest"}}}},"responses":{"200":{"description":"Agent updated.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentDetail"}}}},"400":{"$ref":"#/components/responses/BadRequestError"},"401":{"$ref":"#/components/responses/UnauthorizedError"},"404":{"$ref":"#/components/responses/NotFoundError"}}}}}}
```

## Delete an Agent

> Deletes an Agent. Fails with 409 if there are active sessions for the agent.

```json
{"openapi":"3.0.0","info":{"title":"Ojin REST API","version":"v1.0.0"},"tags":[{"name":"Agents","description":"Operations related to Agents (Agent Service v1) — configuration, providers, and call history."}],"servers":[{"url":"https://api.ojin.ai/v1","description":"Main backend server, version 1."}],"security":[{"APIKeyAuth":[]}],"components":{"securitySchemes":{"APIKeyAuth":{"type":"apiKey","in":"header","name":"X-API-Key","description":"API key for authenticated access to the Ojin REST API."}},"parameters":{"AgentIdPathParameter":{"name":"agent_id","in":"path","required":true,"description":"The unique identifier (UUID) of the Agent.","schema":{"type":"string","format":"uuid"}}},"responses":{"NoContentSuccess":{"description":"Operation successful, no content to return."},"UnauthorizedError":{"description":"Authentication token is missing, invalid, or expired.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"NotFoundError":{"description":"The requested resource was not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"ConflictError":{"description":"Conflict with the current state of the resource.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"schemas":{"ErrorResponse":{"type":"object","properties":{"code":{"type":"string","description":"A short, machine-readable error code string."},"message":{"type":"string","description":"A human-readable description of the error."},"details":{"type":"object","additionalProperties":true,"nullable":true,"description":"Optional. Additional structured details about the error."}},"required":["code","message"]}}},"paths":{"/agents/{agent_id}":{"delete":{"tags":["Agents"],"summary":"Delete an Agent","description":"Deletes an Agent. Fails with 409 if there are active sessions for the agent.","operationId":"deleteAgent","parameters":[{"$ref":"#/components/parameters/AgentIdPathParameter"}],"responses":{"204":{"$ref":"#/components/responses/NoContentSuccess"},"401":{"$ref":"#/components/responses/UnauthorizedError"},"404":{"$ref":"#/components/responses/NotFoundError"},"409":{"$ref":"#/components/responses/ConflictError"}}}}}}
```

## PATCH /agents/{agent\_id}/status

> Publish or unpublish an Agent

```json
{"openapi":"3.0.0","info":{"title":"Ojin REST API","version":"v1.0.0"},"tags":[{"name":"Agents","description":"Operations related to Agents (Agent Service v1) — configuration, providers, and call history."}],"servers":[{"url":"https://api.ojin.ai/v1","description":"Main backend server, version 1."}],"security":[{"APIKeyAuth":[]}],"components":{"securitySchemes":{"APIKeyAuth":{"type":"apiKey","in":"header","name":"X-API-Key","description":"API key for authenticated access to the Ojin REST API."}},"parameters":{"AgentIdPathParameter":{"name":"agent_id","in":"path","required":true,"description":"The unique identifier (UUID) of the Agent.","schema":{"type":"string","format":"uuid"}}},"schemas":{"UpdateAgentStatusRequest":{"type":"object","description":"Payload for toggling Agent publication status.","properties":{"status":{"$ref":"#/components/schemas/AgentStatus"}},"required":["status"]},"AgentStatus":{"type":"string","enum":["published","unpublished"],"description":"The publication status of an agent. Published agents are available for connections; unpublished agents are not."},"AgentDetail":{"allOf":[{"$ref":"#/components/schemas/AgentBase"},{"type":"object","description":"Full Agent detail including configuration, STS credentials flags, LLM tool wiring.","properties":{"language":{"$ref":"#/components/schemas/AgentLanguage"},"system_prompt":{"type":"string","maxLength":10000,"nullable":true},"tts_provider":{"type":"string"},"voice_id":{"type":"string","maxLength":255,"nullable":true},"behaviour":{"allOf":[{"$ref":"#/components/schemas/AgentBehaviour"}],"nullable":true},"advanced":{"allOf":[{"$ref":"#/components/schemas/AgentAdvanced"}],"nullable":true},"sts_provider":{"type":"string","maxLength":100,"nullable":true},"sts_config_id":{"type":"string","maxLength":255,"nullable":true},"sts_api_key_set":{"type":"boolean","description":"True when an encrypted STS API key is persisted for this agent. The raw key is never returned."},"provider_config":{"type":"object","additionalProperties":true,"nullable":true},"preview_url":{"type":"string","maxLength":2048,"nullable":true,"description":"A direct URL or a bare asset-ID UUID. The public agent endpoint resolves UUIDs to signed S3 URLs before returning them to clients."},"starts_conversation":{"type":"boolean"},"session_limit_seconds":{"type":"integer","minimum":1,"maximum":86400},"max_concurrency":{"oneOf":[{"type":"integer","enum":[-1]},{"type":"integer","minimum":1,"maximum":100}],"description":"Maximum concurrent sessions. -1 means unbounded (no limit); valid positive values are 1–100."},"auth_enabled":{"type":"boolean"},"auth_secret_set":{"type":"boolean","description":"True when an auth secret is persisted."},"auth_secret":{"type":"string","nullable":true,"description":"Plaintext auth secret. Only returned to the creator immediately after\nagent creation (one-shot). Subsequent reads return null; use auth_secret_set\nto detect whether a secret is configured."},"allowed_hostnames":{"type":"array","nullable":true,"items":{"type":"string","maxLength":255}},"tools":{"type":"array","description":"Per-tool function-calling definitions with delivery + enabled flag.","items":{"$ref":"#/components/schemas/AgentTool"}}},"required":["language","tts_provider","sts_api_key_set","starts_conversation","session_limit_seconds","max_concurrency","auth_enabled","auth_secret_set","tools"]}]},"AgentBase":{"type":"object","description":"Basic representation of an Agent for list views.","properties":{"agent_id":{"type":"string","format":"uuid","readOnly":true},"organization_id":{"type":"string","format":"uuid","readOnly":true},"created_by":{"type":"string","readOnly":true,"description":"Creator's user ID (from external IdP), or `api_key:<id>` when the resource was created via an API key."},"title":{"type":"string","maxLength":255},"description":{"type":"string","maxLength":1000,"nullable":true},"mode":{"$ref":"#/components/schemas/AgentMode"},"status":{"$ref":"#/components/schemas/AgentStatus"},"face_id":{"type":"string","format":"uuid","nullable":true},"created_at":{"type":"string","format":"date-time","readOnly":true},"updated_at":{"type":"string","format":"date-time","readOnly":true}},"required":["agent_id","organization_id","created_by","title","mode","status","face_id","created_at","updated_at"]},"AgentMode":{"type":"string","enum":["ojin","third_party"],"description":"The operating mode of an agent. Ojin mode uses the built-in pipeline; third_party mode delegates to an external STS provider."},"AgentLanguage":{"type":"string","enum":["en","de","fr","es","ar","ja"],"description":"Spoken language code for the agent. Drives Deepgram STT language,\nElevenLabs voice filtering in the dashboard, and an LLM\nsystem-prompt suffix that pins the response language.\n"},"AgentBehaviour":{"type":"object","description":"Conversational behaviour knobs applied to an Agent.","properties":{"greeting":{"type":"string","maxLength":2000,"nullable":true},"greeting_reference":{"type":"string","maxLength":2000,"nullable":true},"greeting_mode":{"type":"string","enum":["verbatim","composed"],"nullable":true},"nudge_message":{"type":"string","maxLength":2000,"nullable":true},"nudge_reference":{"type":"string","maxLength":2000,"nullable":true},"nudge_mode":{"type":"string","enum":["verbatim","composed"],"nullable":true},"farewell_message":{"type":"string","maxLength":2000,"nullable":true},"farewell_reference":{"type":"string","maxLength":2000,"nullable":true},"farewell_mode":{"type":"string","enum":["verbatim","composed"],"nullable":true},"max_conversation_duration_seconds":{"type":"integer","minimum":1,"maximum":86400,"nullable":true},"inactivity_nudge_seconds":{"type":"integer","minimum":1,"maximum":3600,"nullable":true},"allow_interruption":{"type":"boolean","nullable":true}}},"AgentAdvanced":{"type":"object","description":"Advanced configuration for an Agent (video pipeline + LLM tuning).","properties":{"max_video_source_dimension":{"type":"integer","minimum":1,"nullable":true},"llm_max_tokens":{"type":"integer","minimum":64,"maximum":4096,"nullable":true,"description":"Maximum completion tokens the managed (Ojin-mode) LLM may generate per spoken reply. Raise it if answers get cut off mid-sentence; lower it to keep replies short. Defaults to 384 when unset."}}},"AgentTool":{"type":"object","additionalProperties":false,"description":"A function-calling tool exposed by the agent to the LLM, with per-tool delivery and enabled flag.","properties":{"tool_id":{"type":"string","format":"uuid","readOnly":true},"enabled":{"type":"boolean","default":true,"description":"When false, the tool is hidden from the LLM but its delivery config is preserved."},"type":{"type":"string","enum":["function"],"default":"function"},"function":{"$ref":"#/components/schemas/ToolFunction"},"delivery":{"$ref":"#/components/schemas/ToolDelivery"}},"required":["tool_id","enabled","type","function","delivery"]},"ToolFunction":{"type":"object","additionalProperties":false,"description":"OpenAI / Anthropic tool function definition.","properties":{"name":{"type":"string","maxLength":64,"pattern":"^[a-zA-Z0-9_-]+$","description":"Function name. Must match the LLM tool-name regex."},"description":{"type":"string","maxLength":1024,"nullable":true},"parameters":{"type":"object","additionalProperties":true,"nullable":true,"description":"JSON Schema for the function's parameters (opaque pass-through)."}},"required":["name"]},"ToolDelivery":{"oneOf":[{"$ref":"#/components/schemas/ClientToolDelivery"},{"$ref":"#/components/schemas/WebhookToolDelivery"}],"discriminator":{"propertyName":"mode","mapping":{"client":"#/components/schemas/ClientToolDelivery","webhook":"#/components/schemas/WebhookToolDelivery"}}},"ClientToolDelivery":{"type":"object","additionalProperties":false,"description":"Tool calls are delivered to the in-browser widget as an `ojinToolCall` CustomEvent.","properties":{"mode":{"type":"string","enum":["client"]}},"required":["mode"]},"WebhookToolDelivery":{"type":"object","additionalProperties":false,"description":"Tool calls are POSTed to an external webhook URL.","properties":{"mode":{"type":"string","enum":["webhook"]},"url":{"type":"string","format":"uri","maxLength":2048},"wait_for_response":{"type":"boolean","default":false,"description":"When true, the LLM waits for the webhook's response and incorporates it\ninto the conversation. When false, the webhook is fired-and-forgotten and\nthe LLM receives a neutral acknowledgement immediately."}},"required":["mode","url"]},"ErrorResponse":{"type":"object","properties":{"code":{"type":"string","description":"A short, machine-readable error code string."},"message":{"type":"string","description":"A human-readable description of the error."},"details":{"type":"object","additionalProperties":true,"nullable":true,"description":"Optional. Additional structured details about the error."}},"required":["code","message"]}},"responses":{"BadRequestError":{"description":"Invalid request payload or parameters.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"UnauthorizedError":{"description":"Authentication token is missing, invalid, or expired.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"NotFoundError":{"description":"The requested resource was not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"paths":{"/agents/{agent_id}/status":{"patch":{"tags":["Agents"],"summary":"Publish or unpublish an Agent","operationId":"updateAgentStatus","parameters":[{"$ref":"#/components/parameters/AgentIdPathParameter"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateAgentStatusRequest"}}}},"responses":{"200":{"description":"Agent status updated.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentDetail"}}}},"400":{"$ref":"#/components/responses/BadRequestError"},"401":{"$ref":"#/components/responses/UnauthorizedError"},"404":{"$ref":"#/components/responses/NotFoundError"}}}}}}
```

## GET /agents/{agent\_id}/calls

> List call history for an Agent

```json
{"openapi":"3.0.0","info":{"title":"Ojin REST API","version":"v1.0.0"},"tags":[{"name":"Agents","description":"Operations related to Agents (Agent Service v1) — configuration, providers, and call history."}],"servers":[{"url":"https://api.ojin.ai/v1","description":"Main backend server, version 1."}],"security":[{"APIKeyAuth":[]}],"components":{"securitySchemes":{"APIKeyAuth":{"type":"apiKey","in":"header","name":"X-API-Key","description":"API key for authenticated access to the Ojin REST API."}},"parameters":{"AgentIdPathParameter":{"name":"agent_id","in":"path","required":true,"description":"The unique identifier (UUID) of the Agent.","schema":{"type":"string","format":"uuid"}},"LimitQueryParameter":{"name":"limit","in":"query","required":false,"description":"Number of items to return per page.","schema":{"type":"integer","default":20,"minimum":1,"maximum":100}},"OffsetQueryParameter":{"name":"offset","in":"query","required":false,"description":"Number of items to skip for pagination.","schema":{"type":"integer","default":0,"minimum":0}}},"schemas":{"CallRecord":{"type":"object","description":"A single historical call for an Agent (one call = one agent session).","properties":{"call_id":{"type":"string","format":"uuid"},"agent_id":{"type":"string","format":"uuid"},"organization_id":{"type":"string","format":"uuid"},"session_id":{"type":"string","format":"uuid","nullable":true},"applied_session_limit_seconds":{"type":"integer"},"client_user_ref":{"type":"string","nullable":true},"started_at":{"type":"string","format":"date-time"},"ended_at":{"type":"string","format":"date-time","nullable":true},"status":{"type":"string","description":"e.g. 'active', 'completed', 'failed'"},"termination_reason":{"type":"string","nullable":true},"failure_reason":{"type":"string","nullable":true},"participant_count":{"type":"integer"},"metadata":{"type":"object","additionalProperties":true,"nullable":true}},"required":["call_id","agent_id","organization_id","applied_session_limit_seconds","started_at","status","participant_count"]},"ErrorResponse":{"type":"object","properties":{"code":{"type":"string","description":"A short, machine-readable error code string."},"message":{"type":"string","description":"A human-readable description of the error."},"details":{"type":"object","additionalProperties":true,"nullable":true,"description":"Optional. Additional structured details about the error."}},"required":["code","message"]}},"responses":{"UnauthorizedError":{"description":"Authentication token is missing, invalid, or expired.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"NotFoundError":{"description":"The requested resource was not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"paths":{"/agents/{agent_id}/calls":{"get":{"tags":["Agents"],"summary":"List call history for an Agent","operationId":"listAgentCalls","parameters":[{"$ref":"#/components/parameters/AgentIdPathParameter"},{"$ref":"#/components/parameters/LimitQueryParameter"},{"$ref":"#/components/parameters/OffsetQueryParameter"},{"name":"started_after","in":"query","required":false,"description":"ISO-8601 date/datetime lower bound on `started_at`.","schema":{"type":"string","format":"date-time"}},{"name":"started_before","in":"query","required":false,"description":"ISO-8601 date/datetime upper bound on `started_at`.","schema":{"type":"string","format":"date-time"}}],"responses":{"200":{"description":"Paginated call history.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/CallRecord"}},"pagination":{"type":"object","properties":{"limit":{"type":"integer"},"offset":{"type":"integer"},"total_items":{"type":"integer"}},"required":["limit","offset","total_items"]}},"required":["data","pagination"]}}}},"401":{"$ref":"#/components/responses/UnauthorizedError"},"404":{"$ref":"#/components/responses/NotFoundError"}}}}}}
```

## List STS providers

> Lists available STS (speech-to-speech) providers and the fields each expects.

```json
{"openapi":"3.0.0","info":{"title":"Ojin REST API","version":"v1.0.0"},"tags":[{"name":"Agents","description":"Operations related to Agents (Agent Service v1) — configuration, providers, and call history."}],"servers":[{"url":"https://api.ojin.ai/v1","description":"Main backend server, version 1."}],"security":[{"APIKeyAuth":[]}],"components":{"securitySchemes":{"APIKeyAuth":{"type":"apiKey","in":"header","name":"X-API-Key","description":"API key for authenticated access to the Ojin REST API."}},"schemas":{"AgentProvider":{"type":"object","description":"STS provider descriptor (returned by GET /agents/providers).","properties":{"slug":{"type":"string"},"label":{"type":"string"},"required_persisted":{"type":"array","items":{"type":"string"},"description":"Provider-specific fields that must be persisted in provider_config."},"optional_persisted":{"type":"array","items":{"type":"string"},"description":"Provider-specific fields that may be persisted in provider_config."},"session_fields":{"type":"array","items":{"type":"string"},"description":"Provider-specific fields that must be supplied per-session."},"status":{"type":"string"}},"required":["slug","label","required_persisted","optional_persisted","session_fields","status"]},"ErrorResponse":{"type":"object","properties":{"code":{"type":"string","description":"A short, machine-readable error code string."},"message":{"type":"string","description":"A human-readable description of the error."},"details":{"type":"object","additionalProperties":true,"nullable":true,"description":"Optional. Additional structured details about the error."}},"required":["code","message"]}},"responses":{"UnauthorizedError":{"description":"Authentication token is missing, invalid, or expired.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"paths":{"/agents/providers":{"get":{"tags":["Agents"],"summary":"List STS providers","description":"Lists available STS (speech-to-speech) providers and the fields each expects.","operationId":"listAgentProviders","responses":{"200":{"description":"Provider catalogue.","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/AgentProvider"}}}}},"401":{"$ref":"#/components/responses/UnauthorizedError"}}}}}}
```

## Verify third-party provider credentials for an Agent

> Makes a live API call to the agent's configured STS provider using the stored\
> API key and config ID. Returns whether the credentials are valid and any error\
> detail. Does not modify the agent.

```json
{"openapi":"3.0.0","info":{"title":"Ojin REST API","version":"v1.0.0"},"tags":[{"name":"Agents","description":"Operations related to Agents (Agent Service v1) — configuration, providers, and call history."}],"servers":[{"url":"https://api.ojin.ai/v1","description":"Main backend server, version 1."}],"security":[{"APIKeyAuth":[]}],"components":{"securitySchemes":{"APIKeyAuth":{"type":"apiKey","in":"header","name":"X-API-Key","description":"API key for authenticated access to the Ojin REST API."}},"parameters":{"AgentIdPathParameter":{"name":"agent_id","in":"path","required":true,"description":"The unique identifier (UUID) of the Agent.","schema":{"type":"string","format":"uuid"}}},"schemas":{"VerifyProviderResult":{"type":"object","description":"Result of a live credential verification check against a third-party STS provider.","properties":{"valid":{"type":"boolean"},"provider":{"type":"string"},"error":{"type":"string","description":"Human-readable reason when valid is false."}},"required":["valid","provider"]},"ErrorResponse":{"type":"object","properties":{"code":{"type":"string","description":"A short, machine-readable error code string."},"message":{"type":"string","description":"A human-readable description of the error."},"details":{"type":"object","additionalProperties":true,"nullable":true,"description":"Optional. Additional structured details about the error."}},"required":["code","message"]}},"responses":{"BadRequestError":{"description":"Invalid request payload or parameters.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"UnauthorizedError":{"description":"Authentication token is missing, invalid, or expired.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"NotFoundError":{"description":"The requested resource was not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"BadGatewayError":{"description":"Upstream service (e.g. WebRTC room provider, agent orchestrator) returned an error or was unreachable.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"paths":{"/agents/{agent_id}/verify-provider":{"post":{"tags":["Agents"],"summary":"Verify third-party provider credentials for an Agent","description":"Makes a live API call to the agent's configured STS provider using the stored\nAPI key and config ID. Returns whether the credentials are valid and any error\ndetail. Does not modify the agent.","operationId":"verifyAgentProvider","parameters":[{"$ref":"#/components/parameters/AgentIdPathParameter"}],"responses":{"200":{"description":"Verification result (may be valid or invalid).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/VerifyProviderResult"}}}},"400":{"$ref":"#/components/responses/BadRequestError"},"401":{"$ref":"#/components/responses/UnauthorizedError"},"404":{"$ref":"#/components/responses/NotFoundError"},"502":{"$ref":"#/components/responses/BadGatewayError"}}}}}}
```

## Public Widget Endpoints

These unauthenticated endpoints are intended for browser clients such as the Ojin widget.

## Retrieve a Model Configuration (Public)

> Retrieves a specific Model Configuration by ID. This is the public variant of\
> \`GET /model-configs/{model\_config\_id}\`, designed for use by the Ojin widget\
> embedded on third-party domains. Uses permissive CORS and requires no authentication.\
> Resources are accessed by their UUID.\
> \
> Internal fields (organization\_id, created\_by, model\_id) are stripped from the response. The widget chains through \`\`model\_variant\_id\`\` and calls \`\`GET /public/model-variants/{model\_variant\_id}\`\` for catalog metadata (rendered frame size, etc.).

```json
{"openapi":"3.0.0","info":{"title":"Ojin REST API","version":"v1.0.0"},"tags":[{"name":"Public API","description":"Public endpoints for unauthenticated browser clients such as the Ojin widget. These routes use permissive CORS (origin: '*')\nto allow the widget to be embedded on any third-party domain. No authentication is required."}],"servers":[{"url":"https://api.ojin.ai/v1","description":"Main backend server, version 1."}],"security":[],"paths":{"/public/model-configs/{model_config_id}":{"get":{"tags":["Public API"],"summary":"Retrieve a Model Configuration (Public)","description":"Retrieves a specific Model Configuration by ID. This is the public variant of\n`GET /model-configs/{model_config_id}`, designed for use by the Ojin widget\nembedded on third-party domains. Uses permissive CORS and requires no authentication.\nResources are accessed by their UUID.\n\nInternal fields (organization_id, created_by, model_id) are stripped from the response. The widget chains through ``model_variant_id`` and calls ``GET /public/model-variants/{model_variant_id}`` for catalog metadata (rendered frame size, etc.).","operationId":"publicGetModelConfigById","parameters":[{"$ref":"#/components/parameters/ModelConfigIdPathParameter"}],"responses":{"200":{"description":"Model Configuration details (without internal fields).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicModelConfig"}}}},"404":{"$ref":"#/components/responses/NotFoundError"}}}}},"components":{"parameters":{"ModelConfigIdPathParameter":{"name":"model_config_id","in":"path","required":true,"description":"The unique identifier (UUID) of the Model Configuration.","schema":{"type":"string","format":"uuid"}}},"schemas":{"PublicModelConfig":{"type":"object","description":"Public variant of ModelConfig for widget embedding.\nStrips internal fields (organization_id, created_by, model_id) — the widget\nchains through ``model_variant_id`` and calls ``GET /public/model-variants/{model_variant_id}``\nwhen it needs the parent ``Model`` reference.","properties":{"model_config_id":{"type":"string","format":"uuid","readOnly":true,"description":"Server-generated unique ID."},"model_variant_id":{"type":"string","description":"ID of the ModelVariant being configured."},"title":{"type":"string","description":"Title for the configuration."},"model_configurations":{"type":"object","additionalProperties":true,"description":"Parameters for the model variant.","default":{}},"created_at":{"type":"string","format":"date-time","readOnly":true,"description":"Timestamp of creation."},"updated_at":{"type":"string","format":"date-time","readOnly":true,"description":"Timestamp of last update."}},"required":["model_config_id","model_variant_id","title","model_configurations","created_at","updated_at"]},"ErrorResponse":{"type":"object","properties":{"code":{"type":"string","description":"A short, machine-readable error code string."},"message":{"type":"string","description":"A human-readable description of the error."},"details":{"type":"object","additionalProperties":true,"nullable":true,"description":"Optional. Additional structured details about the error."}},"required":["code","message"]}},"responses":{"NotFoundError":{"description":"The requested resource was not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}}
```

## Retrieve Asset Metadata with Download URL (Public)

> Retrieves metadata for a specific Asset, including a pre-signed download URL.\
> This is the public variant of \`GET /assets/{asset\_id}/download\`, designed for use by the\
> Ojin widget embedded on third-party domains. Uses permissive CORS and requires\
> no authentication. Resources are accessed by their UUID.\
> \
> Internal fields (organization\_id, created\_by) are stripped from the response.

```json
{"openapi":"3.0.0","info":{"title":"Ojin REST API","version":"v1.0.0"},"tags":[{"name":"Public API","description":"Public endpoints for unauthenticated browser clients such as the Ojin widget. These routes use permissive CORS (origin: '*')\nto allow the widget to be embedded on any third-party domain. No authentication is required."}],"servers":[{"url":"https://api.ojin.ai/v1","description":"Main backend server, version 1."}],"security":[],"paths":{"/public/assets/{asset_id}":{"get":{"tags":["Public API"],"summary":"Retrieve Asset Metadata with Download URL (Public)","description":"Retrieves metadata for a specific Asset, including a pre-signed download URL.\nThis is the public variant of `GET /assets/{asset_id}/download`, designed for use by the\nOjin widget embedded on third-party domains. Uses permissive CORS and requires\nno authentication. Resources are accessed by their UUID.\n\nInternal fields (organization_id, created_by) are stripped from the response.","operationId":"publicGetAssetById","parameters":[{"$ref":"#/components/parameters/AssetIdPathParameter"}],"responses":{"200":{"description":"Successfully retrieved Asset metadata with download URL (without internal fields).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicAsset"}}}},"404":{"$ref":"#/components/responses/NotFoundError"}}}}},"components":{"parameters":{"AssetIdPathParameter":{"name":"asset_id","in":"path","required":true,"description":"The unique identifier (UUID) of the Asset.","schema":{"type":"string","format":"uuid"}}},"schemas":{"PublicAsset":{"type":"object","description":"Public variant of Asset for widget embedding.\nStrips internal fields (organization_id, created_by) and includes a pre-signed download URL.","properties":{"asset_id":{"type":"string","format":"uuid","readOnly":true,"description":"Unique identifier for the Asset."},"name":{"type":"string","description":"The original file name of the Asset."},"category":{"type":"string","description":"Primary category of the Asset (e.g., 'video', 'image', 'weight')."},"content_type":{"type":"string","description":"The MIME type of the asset."},"size_bytes":{"type":"integer","format":"int64","description":"The size of the asset in bytes."},"etag":{"type":"string","description":"The ETag of the S3 object."},"created_at":{"type":"string","format":"date-time","readOnly":true,"description":"Timestamp of creation."},"updated_at":{"type":"string","format":"date-time","readOnly":true,"description":"Timestamp of last update."},"asset_url":{"type":"string","format":"uri","readOnly":true,"description":"A temporary, pre-signed URL to download the asset's content. This URL will expire."}},"required":["asset_id","name","category","content_type","size_bytes","etag","created_at","updated_at","asset_url"]},"ErrorResponse":{"type":"object","properties":{"code":{"type":"string","description":"A short, machine-readable error code string."},"message":{"type":"string","description":"A human-readable description of the error."},"details":{"type":"object","additionalProperties":true,"nullable":true,"description":"Optional. Additional structured details about the error."}},"required":["code","message"]}},"responses":{"NotFoundError":{"description":"The requested resource was not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}}
```

## Schemas

## The ModelVariant object

```json
{"openapi":"3.0.0","info":{"title":"Ojin REST API","version":"v1.0.0"},"components":{"schemas":{"ModelVariant":{"type":"object","description":"Represents a specific variant of a Model.","properties":{"model_variant_id":{"type":"string","description":"Client-provided unique identifier (e.g., \"ojin/oris-v1/standard\")."},"model_id":{"type":"string","description":"Identifier of the parent Model. NOT NULL."},"title":{"type":"string","description":"Display name for the variant. NOT NULL, unique per model_id."},"description":{"type":"object","additionalProperties":{"type":"string"},"description":"UI display texts. NOT NULL, defaults to '{}'."},"preview_media_url":{"type":"string","format":"url","nullable":true,"description":"URL for a preview media."},"configuration_schema":{"type":"object","description":"JSON schema for ModelConfig.model_configurations. NOT NULL, defaults to '{}'.","additionalProperties":true},"tags":{"type":"array","items":{"type":"string"},"description":"List of descriptive tags. NOT NULL, defaults to '[]'."},"status":{"type":"string","description":"Status (e.g., 'available', 'deprecated'). NOT NULL. List managed in code."},"created_at":{"type":"string","format":"date-time","description":"Timestamp of creation. Server-generated. Defaults to CURRENT_TIMESTAMP.","readOnly":true},"updated_at":{"type":"string","format":"date-time","description":"Timestamp of last update. Server-generated. Auto-updates.","readOnly":true}},"required":["model_variant_id","model_id","title","description","configuration_schema","tags","status","created_at","updated_at"]}}}}
```

## The ModelConfig object

```json
{"openapi":"3.0.0","info":{"title":"Ojin REST API","version":"v1.0.0"},"components":{"schemas":{"ModelConfig":{"type":"object","description":"Represents a specific configuration of a ModelVariant.","properties":{"model_config_id":{"type":"string","format":"uuid","readOnly":true,"description":"Server-generated unique ID."},"organization_id":{"type":"string","readOnly":true,"description":"Owning organization ID (from external IdP)."},"model_id":{"type":"string","readOnly":true,"description":"ID of the parent Model, derived from the Model Variant."},"model_variant_id":{"type":"string","description":"ID of the ModelVariant being configured. NOT NULL."},"created_by":{"type":"string","readOnly":true,"description":"Creator's user ID (from external IdP), or `api_key:<id>` when created via an API key."},"title":{"type":"string","description":"Title for the configuration. NOT NULL, unique per organization_id."},"model_configurations":{"type":"object","additionalProperties":true,"description":"Parameters for the model variant, adhering to its schema. NOT NULL, defaults to '{}'.","default":{}},"created_at":{"type":"string","format":"date-time","readOnly":true,"description":"Timestamp of creation. Defaults to CURRENT_TIMESTAMP."},"updated_at":{"type":"string","format":"date-time","readOnly":true,"description":"Timestamp of last update. Auto-updates."}},"required":["model_config_id","organization_id","model_id","model_variant_id","created_by","title","model_configurations","created_at","updated_at"]}}}}
```

## The ModelConfigCreationRequest object

```json
{"openapi":"3.0.0","info":{"title":"Ojin REST API","version":"v1.0.0"},"components":{"schemas":{"ModelConfigCreationRequest":{"type":"object","description":"Payload for creating a new Model Configuration.","properties":{"title":{"type":"string"},"model_variant_id":{"type":"string"},"model_configurations":{"type":"object","additionalProperties":true,"default":{}}},"required":["title","model_variant_id"]}}}}
```

## The ModelConfigUpdateRequest object

```json
{"openapi":"3.0.0","info":{"title":"Ojin REST API","version":"v1.0.0"},"components":{"schemas":{"ModelConfigUpdateRequest":{"type":"object","description":"Payload for updating a Model Configuration (title and parameters only).","properties":{"title":{"type":"string"},"model_configurations":{"type":"object","additionalProperties":true}},"required":["title","model_configurations"]}}}}
```

## The Pagination object

```json
{"openapi":"3.0.0","info":{"title":"Ojin REST API","version":"v1.0.0"},"components":{"schemas":{"Pagination":{"type":"object","properties":{"limit":{"type":"integer","description":"The number of items returned in the current page."},"offset":{"type":"integer","description":"The number of items skipped before starting the current page."},"total_items":{"type":"integer","description":"The total number of items available that match the query."}},"required":["limit","offset","total_items"]}}}}
```

## The Asset object

```json
{"openapi":"3.0.0","info":{"title":"Ojin REST API","version":"v1.0.0"},"components":{"schemas":{"Asset":{"type":"object","description":"Represents a digital asset managed by the Core API.","properties":{"asset_id":{"type":"string","format":"uuid","description":"Unique identifier for the Asset, generated during upload initiation.","readOnly":true},"organization_id":{"type":"string","description":"ID of the Organisation (from external IdP) that owns this Asset. Server-set.","readOnly":true},"created_by":{"type":"string","description":"User ID of the creator (from external IdP), or `api_key:<id>` when created via an API key. Server-set.","readOnly":true},"name":{"type":"string","description":"The original file name of the Asset. NOT NULL."},"category":{"type":"string","description":"Primary category of the Asset (e.g., 'video', 'image', 'weight'). NOT NULL. List of values managed in code."},"content_type":{"type":"string","description":"The MIME type of the asset. NOT NULL."},"size_bytes":{"type":"integer","format":"int64","description":"The size of the asset in bytes. NOT NULL."},"etag":{"type":"string","description":"The ETag of the S3 object, used for integrity checking. NOT NULL."},"created_at":{"type":"string","format":"date-time","description":"Timestamp of when this Asset record was created (finalized). Server-generated. Defaults to CURRENT_TIMESTAMP.","readOnly":true},"updated_at":{"type":"string","format":"date-time","description":"Timestamp of the last update to this Asset record. Server-generated. Auto-updates on modification.","readOnly":true},"asset_url":{"type":"string","format":"uri","readOnly":true,"description":"A temporary, pre-signed URL to download the asset's content. This URL will expire."}},"required":["asset_id","organization_id","created_by","name","category","content_type","size_bytes","etag","created_at","updated_at"]}}}}
```

## The AssetInitiateUploadRequest object

```json
{"openapi":"3.0.0","info":{"title":"Ojin REST API","version":"v1.0.0"},"components":{"schemas":{"AssetInitiateUploadRequest":{"type":"object","description":"Payload to initiate a multipart asset upload.","properties":{"name":{"type":"string","description":"Original filename of the asset."},"category":{"type":"string","description":"Category for the asset (e.g., 'video', 'image')."},"content_type":{"type":"string","nullable":true,"description":"Client-declared MIME type of the file."},"size_bytes":{"type":"integer","format":"int64","nullable":true,"description":"Client-declared file size in bytes."}},"required":["name","category"]}}}}
```

## The AssetInitiateUploadResponse object

```json
{"openapi":"3.0.0","info":{"title":"Ojin REST API","version":"v1.0.0"},"components":{"schemas":{"AssetInitiateUploadResponse":{"type":"object","description":"Response from initiating a multipart asset upload.","properties":{"asset_id":{"type":"string","format":"uuid","description":"A unique ID generated by Core API for this asset transaction."},"upload_id":{"type":"string","description":"The multipart upload ID from S3, used to identify this multipart upload session."},"s3_key":{"type":"string","description":"The S3 key for this asset."}},"required":["asset_id","upload_id"]}}}}
```

## The AssetSignPartRequest object

```json
{"openapi":"3.0.0","info":{"title":"Ojin REST API","version":"v1.0.0"},"components":{"schemas":{"AssetSignPartRequest":{"type":"object","description":"Payload to get a pre-signed URL for uploading a specific part of a multipart upload.","properties":{"s3_key":{"type":"string","description":"The S3 key for this asset."},"asset_id":{"type":"string","format":"uuid","description":"The asset ID from the initiate upload response."},"upload_id":{"type":"string","description":"The multipart upload ID from the initiate upload response."},"part_number":{"type":"integer","minimum":1,"maximum":10000,"description":"The part number for this upload part (1-10000)."}},"required":["asset_id","upload_id","part_number"]}}}}
```

## The AssetSignPartResponse object

```json
{"openapi":"3.0.0","info":{"title":"Ojin REST API","version":"v1.0.0"},"components":{"schemas":{"AssetSignPartResponse":{"type":"object","description":"Response containing the pre-signed URL for uploading a specific part.","properties":{"upload_url":{"type":"string","format":"url","description":"The pre-signed S3 URL to PUT this specific part to."},"part_number":{"type":"integer","description":"The part number this URL is for."}},"required":["upload_url","part_number"]}}}}
```

## The AssetFinalizationRequest object

```json
{"openapi":"3.0.0","info":{"title":"Ojin REST API","version":"v1.0.0"},"components":{"schemas":{"AssetFinalizationRequest":{"type":"object","description":"Payload to finalize a multipart asset upload and create the asset metadata record in DB.","properties":{"asset_id":{"type":"string","format":"uuid","description":"The unique ID received from the 'initiate-upload' step."},"upload_id":{"type":"string","description":"The multipart upload ID from the 'initiate-upload' step."},"name":{"type":"string","description":"The original filename (must be consistent with initiate request)."},"category":{"type":"string","description":"The asset category (must be consistent with initiate request)."},"content_type":{"type":"string","description":"Final confirmed MIME type of the asset."},"size_bytes":{"type":"integer","format":"int64","description":"Final confirmed size of the asset in bytes."},"parts":{"type":"array","items":{"$ref":"#/components/schemas/AssetUploadPart"},"description":"List of all uploaded parts with their ETags, in order.","minItems":1}},"required":["asset_id","upload_id","name","category","content_type","size_bytes","parts"]},"AssetUploadPart":{"type":"object","description":"Information about a completed upload part.","properties":{"part_number":{"type":"integer","minimum":1,"maximum":10000,"description":"The part number that was uploaded."},"etag":{"type":"string","description":"The ETag returned by S3 after successfully uploading this part."}},"required":["part_number","etag"]}}}}
```

## The AssetUploadPart object

```json
{"openapi":"3.0.0","info":{"title":"Ojin REST API","version":"v1.0.0"},"components":{"schemas":{"AssetUploadPart":{"type":"object","description":"Information about a completed upload part.","properties":{"part_number":{"type":"integer","minimum":1,"maximum":10000,"description":"The part number that was uploaded."},"etag":{"type":"string","description":"The ETag returned by S3 after successfully uploading this part."}},"required":["part_number","etag"]}}}}
```

## The IdleVideoGenerationRequest object

```json
{"openapi":"3.0.0","info":{"title":"Ojin REST API","version":"v1.0.0"},"components":{"schemas":{"IdleVideoGenerationRequest":{"type":"object","description":"Request payload to trigger idle video generation.","properties":{"source_asset_id":{"type":"string","format":"uuid","description":"The ID of the source image asset to use for generating the idle video."},"reference_template":{"type":"string","description":"The reference motion template to use (e.g., 'v1', 'v2', 'v3').","enum":["v1","v2","v3"]},"model_variant_id":{"type":"string","maxLength":128,"description":"Model variant the generated idle video is intended for (e.g. 'ojin/oris-portrait'). Used only to label the background job so the Recent Generations list names the right model. Optional; callers without a model context omit it and the job uses a default label."},"target_model_config_id":{"type":"string","format":"uuid","description":"Model config to assign the generated video to as its active idle preview once the job completes. When set, core-api writes model_configurations.preview server-side on completion, so the assignment lands even if the browser closed, navigated away, or refreshed mid-generation. Must be a config owned by the caller's organization. Optional; omit to only create the video without assigning it."}},"required":["source_asset_id"]}}}}
```

## The IdleVideoGenerationResponse object

```json
{"openapi":"3.0.0","info":{"title":"Ojin REST API","version":"v1.0.0"},"components":{"schemas":{"IdleVideoGenerationResponse":{"type":"object","description":"Response from triggering idle video generation.","properties":{"job_id":{"type":"string","format":"uuid","description":"Identifier of the background job created for this generation request."}},"required":["job_id"]}}}}
```

## The AgentBase object

```json
{"openapi":"3.0.0","info":{"title":"Ojin REST API","version":"v1.0.0"},"components":{"schemas":{"AgentBase":{"type":"object","description":"Basic representation of an Agent for list views.","properties":{"agent_id":{"type":"string","format":"uuid","readOnly":true},"organization_id":{"type":"string","format":"uuid","readOnly":true},"created_by":{"type":"string","readOnly":true,"description":"Creator's user ID (from external IdP), or `api_key:<id>` when the resource was created via an API key."},"title":{"type":"string","maxLength":255},"description":{"type":"string","maxLength":1000,"nullable":true},"mode":{"$ref":"#/components/schemas/AgentMode"},"status":{"$ref":"#/components/schemas/AgentStatus"},"face_id":{"type":"string","format":"uuid","nullable":true},"created_at":{"type":"string","format":"date-time","readOnly":true},"updated_at":{"type":"string","format":"date-time","readOnly":true}},"required":["agent_id","organization_id","created_by","title","mode","status","face_id","created_at","updated_at"]},"AgentMode":{"type":"string","enum":["ojin","third_party"],"description":"The operating mode of an agent. Ojin mode uses the built-in pipeline; third_party mode delegates to an external STS provider."},"AgentStatus":{"type":"string","enum":["published","unpublished"],"description":"The publication status of an agent. Published agents are available for connections; unpublished agents are not."}}}}
```

## The AgentDetail object

```json
{"openapi":"3.0.0","info":{"title":"Ojin REST API","version":"v1.0.0"},"components":{"schemas":{"AgentDetail":{"allOf":[{"$ref":"#/components/schemas/AgentBase"},{"type":"object","description":"Full Agent detail including configuration, STS credentials flags, LLM tool wiring.","properties":{"language":{"$ref":"#/components/schemas/AgentLanguage"},"system_prompt":{"type":"string","maxLength":10000,"nullable":true},"tts_provider":{"type":"string"},"voice_id":{"type":"string","maxLength":255,"nullable":true},"behaviour":{"allOf":[{"$ref":"#/components/schemas/AgentBehaviour"}],"nullable":true},"advanced":{"allOf":[{"$ref":"#/components/schemas/AgentAdvanced"}],"nullable":true},"sts_provider":{"type":"string","maxLength":100,"nullable":true},"sts_config_id":{"type":"string","maxLength":255,"nullable":true},"sts_api_key_set":{"type":"boolean","description":"True when an encrypted STS API key is persisted for this agent. The raw key is never returned."},"provider_config":{"type":"object","additionalProperties":true,"nullable":true},"preview_url":{"type":"string","maxLength":2048,"nullable":true,"description":"A direct URL or a bare asset-ID UUID. The public agent endpoint resolves UUIDs to signed S3 URLs before returning them to clients."},"starts_conversation":{"type":"boolean"},"session_limit_seconds":{"type":"integer","minimum":1,"maximum":86400},"max_concurrency":{"oneOf":[{"type":"integer","enum":[-1]},{"type":"integer","minimum":1,"maximum":100}],"description":"Maximum concurrent sessions. -1 means unbounded (no limit); valid positive values are 1–100."},"auth_enabled":{"type":"boolean"},"auth_secret_set":{"type":"boolean","description":"True when an auth secret is persisted."},"auth_secret":{"type":"string","nullable":true,"description":"Plaintext auth secret. Only returned to the creator immediately after\nagent creation (one-shot). Subsequent reads return null; use auth_secret_set\nto detect whether a secret is configured."},"allowed_hostnames":{"type":"array","nullable":true,"items":{"type":"string","maxLength":255}},"tools":{"type":"array","description":"Per-tool function-calling definitions with delivery + enabled flag.","items":{"$ref":"#/components/schemas/AgentTool"}}},"required":["language","tts_provider","sts_api_key_set","starts_conversation","session_limit_seconds","max_concurrency","auth_enabled","auth_secret_set","tools"]}]},"AgentBase":{"type":"object","description":"Basic representation of an Agent for list views.","properties":{"agent_id":{"type":"string","format":"uuid","readOnly":true},"organization_id":{"type":"string","format":"uuid","readOnly":true},"created_by":{"type":"string","readOnly":true,"description":"Creator's user ID (from external IdP), or `api_key:<id>` when the resource was created via an API key."},"title":{"type":"string","maxLength":255},"description":{"type":"string","maxLength":1000,"nullable":true},"mode":{"$ref":"#/components/schemas/AgentMode"},"status":{"$ref":"#/components/schemas/AgentStatus"},"face_id":{"type":"string","format":"uuid","nullable":true},"created_at":{"type":"string","format":"date-time","readOnly":true},"updated_at":{"type":"string","format":"date-time","readOnly":true}},"required":["agent_id","organization_id","created_by","title","mode","status","face_id","created_at","updated_at"]},"AgentMode":{"type":"string","enum":["ojin","third_party"],"description":"The operating mode of an agent. Ojin mode uses the built-in pipeline; third_party mode delegates to an external STS provider."},"AgentStatus":{"type":"string","enum":["published","unpublished"],"description":"The publication status of an agent. Published agents are available for connections; unpublished agents are not."},"AgentLanguage":{"type":"string","enum":["en","de","fr","es","ar","ja"],"description":"Spoken language code for the agent. Drives Deepgram STT language,\nElevenLabs voice filtering in the dashboard, and an LLM\nsystem-prompt suffix that pins the response language.\n"},"AgentBehaviour":{"type":"object","description":"Conversational behaviour knobs applied to an Agent.","properties":{"greeting":{"type":"string","maxLength":2000,"nullable":true},"greeting_reference":{"type":"string","maxLength":2000,"nullable":true},"greeting_mode":{"type":"string","enum":["verbatim","composed"],"nullable":true},"nudge_message":{"type":"string","maxLength":2000,"nullable":true},"nudge_reference":{"type":"string","maxLength":2000,"nullable":true},"nudge_mode":{"type":"string","enum":["verbatim","composed"],"nullable":true},"farewell_message":{"type":"string","maxLength":2000,"nullable":true},"farewell_reference":{"type":"string","maxLength":2000,"nullable":true},"farewell_mode":{"type":"string","enum":["verbatim","composed"],"nullable":true},"max_conversation_duration_seconds":{"type":"integer","minimum":1,"maximum":86400,"nullable":true},"inactivity_nudge_seconds":{"type":"integer","minimum":1,"maximum":3600,"nullable":true},"allow_interruption":{"type":"boolean","nullable":true}}},"AgentAdvanced":{"type":"object","description":"Advanced configuration for an Agent (video pipeline + LLM tuning).","properties":{"max_video_source_dimension":{"type":"integer","minimum":1,"nullable":true},"llm_max_tokens":{"type":"integer","minimum":64,"maximum":4096,"nullable":true,"description":"Maximum completion tokens the managed (Ojin-mode) LLM may generate per spoken reply. Raise it if answers get cut off mid-sentence; lower it to keep replies short. Defaults to 384 when unset."}}},"AgentTool":{"type":"object","additionalProperties":false,"description":"A function-calling tool exposed by the agent to the LLM, with per-tool delivery and enabled flag.","properties":{"tool_id":{"type":"string","format":"uuid","readOnly":true},"enabled":{"type":"boolean","default":true,"description":"When false, the tool is hidden from the LLM but its delivery config is preserved."},"type":{"type":"string","enum":["function"],"default":"function"},"function":{"$ref":"#/components/schemas/ToolFunction"},"delivery":{"$ref":"#/components/schemas/ToolDelivery"}},"required":["tool_id","enabled","type","function","delivery"]},"ToolFunction":{"type":"object","additionalProperties":false,"description":"OpenAI / Anthropic tool function definition.","properties":{"name":{"type":"string","maxLength":64,"pattern":"^[a-zA-Z0-9_-]+$","description":"Function name. Must match the LLM tool-name regex."},"description":{"type":"string","maxLength":1024,"nullable":true},"parameters":{"type":"object","additionalProperties":true,"nullable":true,"description":"JSON Schema for the function's parameters (opaque pass-through)."}},"required":["name"]},"ToolDelivery":{"oneOf":[{"$ref":"#/components/schemas/ClientToolDelivery"},{"$ref":"#/components/schemas/WebhookToolDelivery"}],"discriminator":{"propertyName":"mode","mapping":{"client":"#/components/schemas/ClientToolDelivery","webhook":"#/components/schemas/WebhookToolDelivery"}}},"ClientToolDelivery":{"type":"object","additionalProperties":false,"description":"Tool calls are delivered to the in-browser widget as an `ojinToolCall` CustomEvent.","properties":{"mode":{"type":"string","enum":["client"]}},"required":["mode"]},"WebhookToolDelivery":{"type":"object","additionalProperties":false,"description":"Tool calls are POSTed to an external webhook URL.","properties":{"mode":{"type":"string","enum":["webhook"]},"url":{"type":"string","format":"uri","maxLength":2048},"wait_for_response":{"type":"boolean","default":false,"description":"When true, the LLM waits for the webhook's response and incorporates it\ninto the conversation. When false, the webhook is fired-and-forgotten and\nthe LLM receives a neutral acknowledgement immediately."}},"required":["mode","url"]}}}}
```

## The CreateAgentRequest object

```json
{"openapi":"3.0.0","info":{"title":"Ojin REST API","version":"v1.0.0"},"components":{"schemas":{"CreateAgentRequest":{"type":"object","description":"Payload for creating a new Agent, optionally from a preset.","properties":{"title":{"type":"string","maxLength":255},"mode":{"$ref":"#/components/schemas/AgentMode"},"preset_id":{"type":"string","format":"uuid","description":"Optional preset to seed the new agent from."}},"required":["title"]},"AgentMode":{"type":"string","enum":["ojin","third_party"],"description":"The operating mode of an agent. Ojin mode uses the built-in pipeline; third_party mode delegates to an external STS provider."}}}}
```

## The UpdateAgentRequest object

```json
{"openapi":"3.0.0","info":{"title":"Ojin REST API","version":"v1.0.0"},"components":{"schemas":{"UpdateAgentRequest":{"type":"object","description":"Payload for updating an Agent (partial update — all fields optional).","properties":{"title":{"type":"string","maxLength":255},"description":{"type":"string","maxLength":1000,"nullable":true},"mode":{"$ref":"#/components/schemas/AgentMode"},"face_id":{"type":"string","format":"uuid","nullable":true},"language":{"$ref":"#/components/schemas/AgentLanguage"},"system_prompt":{"type":"string","maxLength":10000,"nullable":true},"tts_provider":{"type":"string","enum":["elevenlabs","ojin"]},"voice_id":{"type":"string","maxLength":255,"nullable":true},"behaviour":{"$ref":"#/components/schemas/AgentBehaviour"},"advanced":{"$ref":"#/components/schemas/AgentAdvanced"},"sts_provider":{"type":"string","maxLength":100,"nullable":true},"sts_api_key":{"type":"string","maxLength":1000,"nullable":true,"writeOnly":true},"sts_config_id":{"type":"string","maxLength":255,"nullable":true},"provider_config":{"type":"object","additionalProperties":true,"nullable":true},"preview_url":{"type":"string","maxLength":2048,"nullable":true,"description":"A direct URL or a bare asset-ID UUID. The public agent endpoint resolves UUIDs to signed S3 URLs before returning them to clients."},"session_limit_seconds":{"type":"integer","minimum":1,"maximum":86400},"max_concurrency":{"oneOf":[{"type":"integer","enum":[-1]},{"type":"integer","minimum":1,"maximum":100}],"description":"Maximum concurrent sessions. -1 means unbounded (no limit); valid positive values are 1–100."},"starts_conversation":{"type":"boolean"},"auth_enabled":{"type":"boolean"},"allowed_hostnames":{"type":"array","items":{"type":"string","maxLength":255}},"tools":{"type":"array","description":"Replace the agent's tools with this array (full-replace semantics).\nOmit to leave tools unchanged. UPSERTs by `function.name`; tools not\npresent in the array are deleted from the agent.","items":{"$ref":"#/components/schemas/AgentToolInput"}}}},"AgentMode":{"type":"string","enum":["ojin","third_party"],"description":"The operating mode of an agent. Ojin mode uses the built-in pipeline; third_party mode delegates to an external STS provider."},"AgentLanguage":{"type":"string","enum":["en","de","fr","es","ar","ja"],"description":"Spoken language code for the agent. Drives Deepgram STT language,\nElevenLabs voice filtering in the dashboard, and an LLM\nsystem-prompt suffix that pins the response language.\n"},"AgentBehaviour":{"type":"object","description":"Conversational behaviour knobs applied to an Agent.","properties":{"greeting":{"type":"string","maxLength":2000,"nullable":true},"greeting_reference":{"type":"string","maxLength":2000,"nullable":true},"greeting_mode":{"type":"string","enum":["verbatim","composed"],"nullable":true},"nudge_message":{"type":"string","maxLength":2000,"nullable":true},"nudge_reference":{"type":"string","maxLength":2000,"nullable":true},"nudge_mode":{"type":"string","enum":["verbatim","composed"],"nullable":true},"farewell_message":{"type":"string","maxLength":2000,"nullable":true},"farewell_reference":{"type":"string","maxLength":2000,"nullable":true},"farewell_mode":{"type":"string","enum":["verbatim","composed"],"nullable":true},"max_conversation_duration_seconds":{"type":"integer","minimum":1,"maximum":86400,"nullable":true},"inactivity_nudge_seconds":{"type":"integer","minimum":1,"maximum":3600,"nullable":true},"allow_interruption":{"type":"boolean","nullable":true}}},"AgentAdvanced":{"type":"object","description":"Advanced configuration for an Agent (video pipeline + LLM tuning).","properties":{"max_video_source_dimension":{"type":"integer","minimum":1,"nullable":true},"llm_max_tokens":{"type":"integer","minimum":64,"maximum":4096,"nullable":true,"description":"Maximum completion tokens the managed (Ojin-mode) LLM may generate per spoken reply. Raise it if answers get cut off mid-sentence; lower it to keep replies short. Defaults to 384 when unset."}}},"AgentToolInput":{"type":"object","additionalProperties":false,"description":"Input shape for an agent tool. `tool_id` is optional — when present and\nmatched (by `function.name`) the existing row is updated in place; when\nabsent or unmatched a new tool is created. `enabled` defaults to true\nand `type` defaults to \"function\" server-side when omitted.","properties":{"tool_id":{"type":"string","format":"uuid"},"enabled":{"type":"boolean"},"type":{"type":"string","enum":["function"]},"function":{"$ref":"#/components/schemas/ToolFunction"},"delivery":{"$ref":"#/components/schemas/ToolDelivery"}},"required":["function","delivery"]},"ToolFunction":{"type":"object","additionalProperties":false,"description":"OpenAI / Anthropic tool function definition.","properties":{"name":{"type":"string","maxLength":64,"pattern":"^[a-zA-Z0-9_-]+$","description":"Function name. Must match the LLM tool-name regex."},"description":{"type":"string","maxLength":1024,"nullable":true},"parameters":{"type":"object","additionalProperties":true,"nullable":true,"description":"JSON Schema for the function's parameters (opaque pass-through)."}},"required":["name"]},"ToolDelivery":{"oneOf":[{"$ref":"#/components/schemas/ClientToolDelivery"},{"$ref":"#/components/schemas/WebhookToolDelivery"}],"discriminator":{"propertyName":"mode","mapping":{"client":"#/components/schemas/ClientToolDelivery","webhook":"#/components/schemas/WebhookToolDelivery"}}},"ClientToolDelivery":{"type":"object","additionalProperties":false,"description":"Tool calls are delivered to the in-browser widget as an `ojinToolCall` CustomEvent.","properties":{"mode":{"type":"string","enum":["client"]}},"required":["mode"]},"WebhookToolDelivery":{"type":"object","additionalProperties":false,"description":"Tool calls are POSTed to an external webhook URL.","properties":{"mode":{"type":"string","enum":["webhook"]},"url":{"type":"string","format":"uri","maxLength":2048},"wait_for_response":{"type":"boolean","default":false,"description":"When true, the LLM waits for the webhook's response and incorporates it\ninto the conversation. When false, the webhook is fired-and-forgotten and\nthe LLM receives a neutral acknowledgement immediately."}},"required":["mode","url"]}}}}
```

## The UpdateAgentStatusRequest object

```json
{"openapi":"3.0.0","info":{"title":"Ojin REST API","version":"v1.0.0"},"components":{"schemas":{"UpdateAgentStatusRequest":{"type":"object","description":"Payload for toggling Agent publication status.","properties":{"status":{"$ref":"#/components/schemas/AgentStatus"}},"required":["status"]},"AgentStatus":{"type":"string","enum":["published","unpublished"],"description":"The publication status of an agent. Published agents are available for connections; unpublished agents are not."}}}}
```

## The AgentProvider object

```json
{"openapi":"3.0.0","info":{"title":"Ojin REST API","version":"v1.0.0"},"components":{"schemas":{"AgentProvider":{"type":"object","description":"STS provider descriptor (returned by GET /agents/providers).","properties":{"slug":{"type":"string"},"label":{"type":"string"},"required_persisted":{"type":"array","items":{"type":"string"},"description":"Provider-specific fields that must be persisted in provider_config."},"optional_persisted":{"type":"array","items":{"type":"string"},"description":"Provider-specific fields that may be persisted in provider_config."},"session_fields":{"type":"array","items":{"type":"string"},"description":"Provider-specific fields that must be supplied per-session."},"status":{"type":"string"}},"required":["slug","label","required_persisted","optional_persisted","session_fields","status"]}}}}
```

## The CallRecord object

```json
{"openapi":"3.0.0","info":{"title":"Ojin REST API","version":"v1.0.0"},"components":{"schemas":{"CallRecord":{"type":"object","description":"A single historical call for an Agent (one call = one agent session).","properties":{"call_id":{"type":"string","format":"uuid"},"agent_id":{"type":"string","format":"uuid"},"organization_id":{"type":"string","format":"uuid"},"session_id":{"type":"string","format":"uuid","nullable":true},"applied_session_limit_seconds":{"type":"integer"},"client_user_ref":{"type":"string","nullable":true},"started_at":{"type":"string","format":"date-time"},"ended_at":{"type":"string","format":"date-time","nullable":true},"status":{"type":"string","description":"e.g. 'active', 'completed', 'failed'"},"termination_reason":{"type":"string","nullable":true},"failure_reason":{"type":"string","nullable":true},"participant_count":{"type":"integer"},"metadata":{"type":"object","additionalProperties":true,"nullable":true}},"required":["call_id","agent_id","organization_id","applied_session_limit_seconds","started_at","status","participant_count"]}}}}
```

## The VerifyProviderResult object

```json
{"openapi":"3.0.0","info":{"title":"Ojin REST API","version":"v1.0.0"},"components":{"schemas":{"VerifyProviderResult":{"type":"object","description":"Result of a live credential verification check against a third-party STS provider.","properties":{"valid":{"type":"boolean"},"provider":{"type":"string"},"error":{"type":"string","description":"Human-readable reason when valid is false."}},"required":["valid","provider"]}}}}
```

## The AgentMode object

```json
{"openapi":"3.0.0","info":{"title":"Ojin REST API","version":"v1.0.0"},"components":{"schemas":{"AgentMode":{"type":"string","enum":["ojin","third_party"],"description":"The operating mode of an agent. Ojin mode uses the built-in pipeline; third_party mode delegates to an external STS provider."}}}}
```

## The AgentStatus object

```json
{"openapi":"3.0.0","info":{"title":"Ojin REST API","version":"v1.0.0"},"components":{"schemas":{"AgentStatus":{"type":"string","enum":["published","unpublished"],"description":"The publication status of an agent. Published agents are available for connections; unpublished agents are not."}}}}
```

## The AgentLanguage object

```json
{"openapi":"3.0.0","info":{"title":"Ojin REST API","version":"v1.0.0"},"components":{"schemas":{"AgentLanguage":{"type":"string","enum":["en","de","fr","es","ar","ja"],"description":"Spoken language code for the agent. Drives Deepgram STT language,\nElevenLabs voice filtering in the dashboard, and an LLM\nsystem-prompt suffix that pins the response language.\n"}}}}
```

## The AgentBehaviour object

```json
{"openapi":"3.0.0","info":{"title":"Ojin REST API","version":"v1.0.0"},"components":{"schemas":{"AgentBehaviour":{"type":"object","description":"Conversational behaviour knobs applied to an Agent.","properties":{"greeting":{"type":"string","maxLength":2000,"nullable":true},"greeting_reference":{"type":"string","maxLength":2000,"nullable":true},"greeting_mode":{"type":"string","enum":["verbatim","composed"],"nullable":true},"nudge_message":{"type":"string","maxLength":2000,"nullable":true},"nudge_reference":{"type":"string","maxLength":2000,"nullable":true},"nudge_mode":{"type":"string","enum":["verbatim","composed"],"nullable":true},"farewell_message":{"type":"string","maxLength":2000,"nullable":true},"farewell_reference":{"type":"string","maxLength":2000,"nullable":true},"farewell_mode":{"type":"string","enum":["verbatim","composed"],"nullable":true},"max_conversation_duration_seconds":{"type":"integer","minimum":1,"maximum":86400,"nullable":true},"inactivity_nudge_seconds":{"type":"integer","minimum":1,"maximum":3600,"nullable":true},"allow_interruption":{"type":"boolean","nullable":true}}}}}}
```

## The AgentAdvanced object

```json
{"openapi":"3.0.0","info":{"title":"Ojin REST API","version":"v1.0.0"},"components":{"schemas":{"AgentAdvanced":{"type":"object","description":"Advanced configuration for an Agent (video pipeline + LLM tuning).","properties":{"max_video_source_dimension":{"type":"integer","minimum":1,"nullable":true},"llm_max_tokens":{"type":"integer","minimum":64,"maximum":4096,"nullable":true,"description":"Maximum completion tokens the managed (Ojin-mode) LLM may generate per spoken reply. Raise it if answers get cut off mid-sentence; lower it to keep replies short. Defaults to 384 when unset."}}}}}}
```

## The AgentTool object

```json
{"openapi":"3.0.0","info":{"title":"Ojin REST API","version":"v1.0.0"},"components":{"schemas":{"AgentTool":{"type":"object","additionalProperties":false,"description":"A function-calling tool exposed by the agent to the LLM, with per-tool delivery and enabled flag.","properties":{"tool_id":{"type":"string","format":"uuid","readOnly":true},"enabled":{"type":"boolean","default":true,"description":"When false, the tool is hidden from the LLM but its delivery config is preserved."},"type":{"type":"string","enum":["function"],"default":"function"},"function":{"$ref":"#/components/schemas/ToolFunction"},"delivery":{"$ref":"#/components/schemas/ToolDelivery"}},"required":["tool_id","enabled","type","function","delivery"]},"ToolFunction":{"type":"object","additionalProperties":false,"description":"OpenAI / Anthropic tool function definition.","properties":{"name":{"type":"string","maxLength":64,"pattern":"^[a-zA-Z0-9_-]+$","description":"Function name. Must match the LLM tool-name regex."},"description":{"type":"string","maxLength":1024,"nullable":true},"parameters":{"type":"object","additionalProperties":true,"nullable":true,"description":"JSON Schema for the function's parameters (opaque pass-through)."}},"required":["name"]},"ToolDelivery":{"oneOf":[{"$ref":"#/components/schemas/ClientToolDelivery"},{"$ref":"#/components/schemas/WebhookToolDelivery"}],"discriminator":{"propertyName":"mode","mapping":{"client":"#/components/schemas/ClientToolDelivery","webhook":"#/components/schemas/WebhookToolDelivery"}}},"ClientToolDelivery":{"type":"object","additionalProperties":false,"description":"Tool calls are delivered to the in-browser widget as an `ojinToolCall` CustomEvent.","properties":{"mode":{"type":"string","enum":["client"]}},"required":["mode"]},"WebhookToolDelivery":{"type":"object","additionalProperties":false,"description":"Tool calls are POSTed to an external webhook URL.","properties":{"mode":{"type":"string","enum":["webhook"]},"url":{"type":"string","format":"uri","maxLength":2048},"wait_for_response":{"type":"boolean","default":false,"description":"When true, the LLM waits for the webhook's response and incorporates it\ninto the conversation. When false, the webhook is fired-and-forgotten and\nthe LLM receives a neutral acknowledgement immediately."}},"required":["mode","url"]}}}}
```

## The AgentToolInput object

```json
{"openapi":"3.0.0","info":{"title":"Ojin REST API","version":"v1.0.0"},"components":{"schemas":{"AgentToolInput":{"type":"object","additionalProperties":false,"description":"Input shape for an agent tool. `tool_id` is optional — when present and\nmatched (by `function.name`) the existing row is updated in place; when\nabsent or unmatched a new tool is created. `enabled` defaults to true\nand `type` defaults to \"function\" server-side when omitted.","properties":{"tool_id":{"type":"string","format":"uuid"},"enabled":{"type":"boolean"},"type":{"type":"string","enum":["function"]},"function":{"$ref":"#/components/schemas/ToolFunction"},"delivery":{"$ref":"#/components/schemas/ToolDelivery"}},"required":["function","delivery"]},"ToolFunction":{"type":"object","additionalProperties":false,"description":"OpenAI / Anthropic tool function definition.","properties":{"name":{"type":"string","maxLength":64,"pattern":"^[a-zA-Z0-9_-]+$","description":"Function name. Must match the LLM tool-name regex."},"description":{"type":"string","maxLength":1024,"nullable":true},"parameters":{"type":"object","additionalProperties":true,"nullable":true,"description":"JSON Schema for the function's parameters (opaque pass-through)."}},"required":["name"]},"ToolDelivery":{"oneOf":[{"$ref":"#/components/schemas/ClientToolDelivery"},{"$ref":"#/components/schemas/WebhookToolDelivery"}],"discriminator":{"propertyName":"mode","mapping":{"client":"#/components/schemas/ClientToolDelivery","webhook":"#/components/schemas/WebhookToolDelivery"}}},"ClientToolDelivery":{"type":"object","additionalProperties":false,"description":"Tool calls are delivered to the in-browser widget as an `ojinToolCall` CustomEvent.","properties":{"mode":{"type":"string","enum":["client"]}},"required":["mode"]},"WebhookToolDelivery":{"type":"object","additionalProperties":false,"description":"Tool calls are POSTed to an external webhook URL.","properties":{"mode":{"type":"string","enum":["webhook"]},"url":{"type":"string","format":"uri","maxLength":2048},"wait_for_response":{"type":"boolean","default":false,"description":"When true, the LLM waits for the webhook's response and incorporates it\ninto the conversation. When false, the webhook is fired-and-forgotten and\nthe LLM receives a neutral acknowledgement immediately."}},"required":["mode","url"]}}}}
```

## The PublicModelConfig object

```json
{"openapi":"3.0.0","info":{"title":"Ojin REST API","version":"v1.0.0"},"components":{"schemas":{"PublicModelConfig":{"type":"object","description":"Public variant of ModelConfig for widget embedding.\nStrips internal fields (organization_id, created_by, model_id) — the widget\nchains through ``model_variant_id`` and calls ``GET /public/model-variants/{model_variant_id}``\nwhen it needs the parent ``Model`` reference.","properties":{"model_config_id":{"type":"string","format":"uuid","readOnly":true,"description":"Server-generated unique ID."},"model_variant_id":{"type":"string","description":"ID of the ModelVariant being configured."},"title":{"type":"string","description":"Title for the configuration."},"model_configurations":{"type":"object","additionalProperties":true,"description":"Parameters for the model variant.","default":{}},"created_at":{"type":"string","format":"date-time","readOnly":true,"description":"Timestamp of creation."},"updated_at":{"type":"string","format":"date-time","readOnly":true,"description":"Timestamp of last update."}},"required":["model_config_id","model_variant_id","title","model_configurations","created_at","updated_at"]}}}}
```

## The PublicAsset object

```json
{"openapi":"3.0.0","info":{"title":"Ojin REST API","version":"v1.0.0"},"components":{"schemas":{"PublicAsset":{"type":"object","description":"Public variant of Asset for widget embedding.\nStrips internal fields (organization_id, created_by) and includes a pre-signed download URL.","properties":{"asset_id":{"type":"string","format":"uuid","readOnly":true,"description":"Unique identifier for the Asset."},"name":{"type":"string","description":"The original file name of the Asset."},"category":{"type":"string","description":"Primary category of the Asset (e.g., 'video', 'image', 'weight')."},"content_type":{"type":"string","description":"The MIME type of the asset."},"size_bytes":{"type":"integer","format":"int64","description":"The size of the asset in bytes."},"etag":{"type":"string","description":"The ETag of the S3 object."},"created_at":{"type":"string","format":"date-time","readOnly":true,"description":"Timestamp of creation."},"updated_at":{"type":"string","format":"date-time","readOnly":true,"description":"Timestamp of last update."},"asset_url":{"type":"string","format":"uri","readOnly":true,"description":"A temporary, pre-signed URL to download the asset's content. This URL will expire."}},"required":["asset_id","name","category","content_type","size_bytes","etag","created_at","updated_at","asset_url"]}}}}
```

## The ErrorResponse object

```json
{"openapi":"3.0.0","info":{"title":"Ojin REST API","version":"v1.0.0"},"components":{"schemas":{"ErrorResponse":{"type":"object","properties":{"code":{"type":"string","description":"A short, machine-readable error code string."},"message":{"type":"string","description":"A human-readable description of the error."},"details":{"type":"object","additionalProperties":true,"nullable":true,"description":"Optional. Additional structured details about the error."}},"required":["code","message"]}}}}
```


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.ojin.ai/getting-started/api.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
