# REST API

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

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/oris-portrait/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"}},"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)."},"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/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)."},"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)."},"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)."},"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.\
> If authenticated, returns variants based on filters (defaulting to all if no filters provided).\
> If unauthenticated, ONLY returns 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.\nIf authenticated, returns variants based on filters (defaulting to all if no filters provided).\nIf unauthenticated, ONLY returns variants with status=\"public\", ignoring 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.\
> If authenticated, returns the variant regardless of status.\
> If unauthenticated, ONLY returns the variant 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.\nIf authenticated, returns the variant regardless of status.\nIf unauthenticated, ONLY returns the variant if status=\"public\". Otherwise 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). 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}},"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). 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}},"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). 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}},"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. Does not include a download URL. 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). 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}},"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. Does not include a download URL. 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"]}},"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"}}}}}}}}}
```

## 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) 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/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) are stripped from the response.","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) that are not needed by the widget.","properties":{"model_config_id":{"type":"string","format":"uuid","readOnly":true,"description":"Server-generated unique ID."},"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."},"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_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}\`, 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}`, 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)."},"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). 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}},"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"]}},"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 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) that are not needed by the widget.","properties":{"model_config_id":{"type":"string","format":"uuid","readOnly":true,"description":"Server-generated unique ID."},"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."},"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_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: 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:

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

The question should be specific, self-contained, and written in natural language.
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.
