openapi: 3.1.0 info: title: Orisigil REST API version: "1.0" description: | Sign media files with C2PA Content Credentials. All operations need a personal API key in the header `Authorization: Bearer osk_...`. Create keys in the dashboard (Connections → API keys). A key has one or more permissions: `sign` (upload, create jobs, delete own files), `read` (account, limits, jobs, assets) and `download` (signed files). Every account sees only its own objects. An ID of a different account answers 404, the same as an unknown ID. Errors have the form `{"error_code": "...", "message": "...", "details": {...}}`. `error_code` is stable; `message` is technical English text and can change. license: name: Proprietary servers: - url: / security: - apiKey: [] tags: - name: account - name: assets - name: signing paths: /api/v1/account: get: tags: [account] summary: Read the account of the key. operationId: getAccount responses: "200": description: The account. content: application/json: schema: { $ref: "#/components/schemas/Account" } "401": { $ref: "#/components/responses/Unauthenticated" } "403": { $ref: "#/components/responses/Forbidden" } /api/v1/limits: get: tags: [account] summary: Read the service limits. They are the same for every account. operationId: getLimits responses: "200": description: The limits. content: application/json: schema: { $ref: "#/components/schemas/Limits" } "401": { $ref: "#/components/responses/Unauthenticated" } "403": { $ref: "#/components/responses/Forbidden" } /api/v1/assets: post: tags: [assets] summary: Upload one file. description: | Send one file in the multipart field `file`. The service reads the body as a stream and checks the real size, not Content-Length. A file of exactly `max_file_size_bytes` is accepted; one byte more answers 413 `file_too_large`. The format comes from the file content. Needs the `sign` permission. operationId: uploadAsset requestBody: required: true content: multipart/form-data: schema: type: object required: [file] properties: file: { type: string, format: binary } responses: "201": description: The stored file. headers: Location: { schema: { type: string } } content: application/json: schema: { $ref: "#/components/schemas/Asset" } "401": { $ref: "#/components/responses/Unauthenticated" } "403": { $ref: "#/components/responses/Forbidden" } "413": description: "`file_too_large`. `details.max_size_bytes` is the limit." content: application/json: schema: { $ref: "#/components/schemas/Error" } "422": description: "`unsupported_format`, `corrupt_input`, `invalid_input` (no file part, or more than one file)." content: application/json: schema: { $ref: "#/components/schemas/Error" } /api/v1/assets/{id}: parameters: - $ref: "#/components/parameters/ID" get: tags: [assets] summary: Read a file description. operationId: getAsset responses: "200": description: The file. content: application/json: schema: { $ref: "#/components/schemas/Asset" } "401": { $ref: "#/components/responses/Unauthenticated" } "404": { $ref: "#/components/responses/NotFound" } delete: tags: [assets] summary: Delete a stored file. description: | The file becomes unavailable at once and is removed by the cleanup. Active jobs that use it as input are canceled with `canceled_by_deletion`. Copies that you downloaded are not affected. Needs the `sign` permission. operationId: deleteAsset responses: "202": description: The deletion is requested. content: application/json: schema: type: object properties: status: { type: string, const: deletion_requested } "401": { $ref: "#/components/responses/Unauthenticated" } "404": { $ref: "#/components/responses/NotFound" } /api/v1/signing-batches: post: tags: [signing] summary: Create a batch of signing jobs. description: | Creates one job per asset in one transaction and answers at once; the signing runs later. Poll the jobs, or the batch, for the result. Send `Idempotency-Key` to make a retry safe: the same key with the same request returns the same batch with `replayed: true`; the same key with a different request answers 409 `idempotency_conflict`. Keys are scoped by account and kept for 24 hours. Needs the `sign` permission. operationId: createBatch parameters: - name: Idempotency-Key in: header required: false schema: { type: string, maxLength: 200 } requestBody: required: true content: application/json: schema: { $ref: "#/components/schemas/CreateBatch" } responses: "202": description: The batch is accepted. headers: Location: { schema: { type: string } } content: application/json: schema: { $ref: "#/components/schemas/Batch" } "401": { $ref: "#/components/responses/Unauthenticated" } "403": { $ref: "#/components/responses/Forbidden" } "404": { $ref: "#/components/responses/NotFound" } "409": description: "`idempotency_conflict`." content: application/json: schema: { $ref: "#/components/schemas/Error" } "422": description: "`invalid_input` or `asset_not_ready`. `details.asset_id` names the asset." content: application/json: schema: { $ref: "#/components/schemas/Error" } /api/v1/signing-batches/{id}: parameters: - $ref: "#/components/parameters/ID" get: tags: [signing] summary: Read a batch and its jobs. operationId: getBatch responses: "200": description: The batch. content: application/json: schema: { $ref: "#/components/schemas/Batch" } "401": { $ref: "#/components/responses/Unauthenticated" } "404": { $ref: "#/components/responses/NotFound" } /api/v1/signing-jobs: get: tags: [signing] summary: List jobs, newest first. operationId: listJobs parameters: - { name: status, in: query, schema: { $ref: "#/components/schemas/JobStatus" } } - { name: source, in: query, schema: { $ref: "#/components/schemas/Source" } } - { name: q, in: query, description: "Part of the file name or of the job ID.", schema: { type: string } } - { name: cursor, in: query, description: "The next_cursor of the previous page.", schema: { type: string } } - { name: limit, in: query, schema: { type: integer, minimum: 1, maximum: 100, default: 25 } } responses: "200": description: A page of jobs. content: application/json: schema: type: object required: [jobs] properties: jobs: { type: array, items: { $ref: "#/components/schemas/Job" } } next_cursor: { type: string, description: "Absent on the last page." } "401": { $ref: "#/components/responses/Unauthenticated" } "422": description: "`invalid_input` (unknown filter or cursor)." content: application/json: schema: { $ref: "#/components/schemas/Error" } /api/v1/signing-jobs/{id}: parameters: - $ref: "#/components/parameters/ID" get: tags: [signing] summary: Read a job. operationId: getJob responses: "200": description: The job. content: application/json: schema: { $ref: "#/components/schemas/Job" } "401": { $ref: "#/components/responses/Unauthenticated" } "404": { $ref: "#/components/responses/NotFound" } /api/v1/signing-jobs/{id}/result: parameters: - $ref: "#/components/parameters/ID" get: tags: [signing] summary: Download the signed file of a completed job. description: Needs the `download` permission. operationId: downloadResult responses: "200": description: The signed file as an attachment. content: image/jpeg: { schema: { type: string, format: binary } } image/png: { schema: { type: string, format: binary } } video/mp4: { schema: { type: string, format: binary } } "401": { $ref: "#/components/responses/Unauthenticated" } "403": { $ref: "#/components/responses/Forbidden" } "404": description: "`not_found`, or `asset_expired` when the file was deleted or its storage period ended." content: application/json: schema: { $ref: "#/components/schemas/Error" } "422": description: "`asset_not_ready`: the job is not completed." content: application/json: schema: { $ref: "#/components/schemas/Error" } components: securitySchemes: apiKey: type: http scheme: bearer description: A personal API key (osk_...). parameters: ID: name: id in: path required: true schema: { type: string, format: uuid } responses: Unauthenticated: description: "`unauthenticated`: no key, an unknown key, or a revoked or expired key." content: application/json: schema: { $ref: "#/components/schemas/Error" } Forbidden: description: "`insufficient_scope`: the key does not have the permission. `details.required_scope` names it." content: application/json: schema: { $ref: "#/components/schemas/Error" } NotFound: description: "`not_found`: the object does not exist or belongs to a different account." content: application/json: schema: { $ref: "#/components/schemas/Error" } schemas: Error: type: object required: [error_code] properties: error_code: { type: string } message: { type: string } details: { type: object, additionalProperties: true } Account: type: object properties: id: { type: string, format: uuid } display_name: { type: string } created_at: { type: string, format: date-time } scopes: { type: array, items: { type: string, enum: [sign, read, download] } } Limits: type: object properties: max_file_size_bytes: { type: integer } formats: { type: array, items: { type: string, enum: [jpeg, png, mp4] } } max_batch_files: { type: integer } asset_retention_seconds: { type: integer } history_retention_seconds: { type: integer } Asset: type: object properties: id: { type: string, format: uuid } name: { type: string, description: "Display name only." } format: { type: string, enum: [jpeg, png, mp4] } media_type: { type: string } size_bytes: { type: integer } sha256: { type: string } has_credentials: { type: boolean, description: "The file already had Content Credentials." } status: { type: string, enum: [ready, deletion_requested, deleted] } created_at: { type: string, format: date-time } expires_at: { type: string, format: date-time } SigningOptions: type: object properties: creation_method: type: string enum: [ai_generated, ai_edited, unspecified] default: unspecified description: The declared way the content was made. The service does not check that it is true. author: { type: string, maxLength: 80 } tool: { type: string, maxLength: 80, description: "The AI tool or model, as you declare it." } note: { type: string, maxLength: 200 } CreateBatch: type: object required: [asset_ids] additionalProperties: false properties: asset_ids: { type: array, minItems: 1, items: { type: string, format: uuid } } options: { $ref: "#/components/schemas/SigningOptions" } JobStatus: type: string enum: [queued, processing, retry_wait, completed, failed, canceled] Source: type: string enum: [web, api, mcp] SigningResult: type: object properties: signature_valid: { type: boolean, description: "The output manifest is cryptographically valid." } trust: { type: string, enum: [trusted, untrusted, not_checked], description: "Separate from validity." } signer: { type: string } test_signature: { type: boolean, description: "A development test certificate signed it; never trusted." } timestamped: { type: boolean } preserved_credentials: { type: boolean, description: "Earlier Content Credentials are kept as an ingredient." } signed_at: { type: string, format: date-time } Job: type: object properties: id: { type: string, format: uuid } batch_id: { type: string, format: uuid } status: { $ref: "#/components/schemas/JobStatus" } source: { $ref: "#/components/schemas/Source" } input_asset_id: { type: [string, "null"], format: uuid } input_name: { type: string } options: { $ref: "#/components/schemas/SigningOptions" } attempts: { type: integer } error_code: { type: string, description: "Set for failed, canceled and retrying jobs." } result: { $ref: "#/components/schemas/SigningResult" } output: type: object properties: size_bytes: { type: integer } available: { type: boolean, description: "False after deletion or after the storage period." } expires_at: { type: string, format: date-time } download_url: { type: string } created_at: { type: string, format: date-time } started_at: { type: string, format: date-time } finished_at: { type: string, format: date-time } status_url: { type: string } Batch: type: object properties: id: { type: string, format: uuid } source: { $ref: "#/components/schemas/Source" } created_at: { type: string, format: date-time } replayed: { type: boolean } status_url: { type: string } jobs: { type: array, items: { $ref: "#/components/schemas/Job" } }