# How It Works URL: /docs/how-it-works *** title: How It Works description: Understand the architecture and the end-to-end workflow for signing and verifying assets with the Que API. icon: "Cog" ----------- import { Step, Steps } from 'fumadocs-ui/components/steps'; import { Callout } from '@/components/ui/callout'; The Que platform is designed for security, scalability, and efficient processing of large media files. This guide breaks down the core components and the typical API workflow you'll follow to sign or verify an asset. #### 1. Initialize the Client Configure your SDK with your API key. The SDK automatically reads the `QUE_API_KEY` environment variable. ```typescript import { Que } from "que-sdk"; const que = new Que({ apiKeyAuth: process.env["QUE_API_KEY_AUTH"] ?? "", }); ``` #### 2. Upload Your Asset Upload a local file directly using the SDK. The SDK handles getting presigned URLs and uploading to S3 automatically. ```typescript // Get presigned URL for upload const presignedResult = await que.getPresignedUrl({ filename: "my-image.jpg", }); // Upload file to S3 using the presigned URL const fileBuffer = await require('fs').promises.readFile("./my-image.jpg"); const uploadResponse = await fetch(presignedResult.uploadUrl, { method: 'PUT', body: fileBuffer, headers: { 'Content-Type': 'image/jpeg', }, }); if (!uploadResponse.ok) { throw new Error('Failed to upload file'); } console.log(`Asset uploaded with key: ${presignedResult.key}`); ``` #### 3. Sign the Asset Create a signed version of your asset with a manifest containing provenance information. ```typescript const signedAsset = await que.signAsset({ asset: { bucket: "your-bucket", // Replace with your actual bucket key: presignedResult.key, }, manifestJson: JSON.stringify({ title: "My Signed Image", assertions: [{ label: "stds.schema-org.CreativeWork", data: { "@context": "https://schema.org", "@type": "CreativeWork", "author": [{ "@type": "Person", "name": "Creator" }] } }] }) }); ``` #### 4. Verify the Asset Check the authenticity and provenance of any signed asset. ```typescript const result = await que.verifyAsset({ asset: { bucket: "your-bucket", // Replace with your actual bucket key: signedAsset.asset.key, }, includeCertificates: true, }); const report = JSON.parse(result.report); console.log(`Asset is trusted: ${report.summary?.isTrusted}`); ``` #### 5. Download Results Access your processed assets through the SDK. * For **signed** assets, download the new file with embedded manifest. * For **verification** results, inspect the detailed provenance report. This workflow ensures that your application remains fast and responsive, even when working with gigabyte-sized files. # Introduction URL: /docs/introduction *** title: Introduction description: Building Trust in a Digital World icon: "Shield" -------------- import { Card, Cards } from 'fumadocs-ui/components/card'; import { File, Folder, Files } from 'fumadocs-ui/components/files'; Trust online is broken. Simple out-of-context fakes, deceptive edits, and false claims of authorship have been eroding trust for years. With AI, it’s easier than ever to create convincing synthetic videos, fabricated audio clips, or AI-generated images that look 100% real. Que provides a proactive solution. Instead of playing the cat-and-mouse game of trying to detect fakes, we focus on **proving what's real**. By attaching a secure, verifiable "birth certificate" and a tamper-evident history to your content, you give your audience a reliable way to verify its origin and history, protecting your brand and combating misinformation at its source. ## How to Use Que We offer a simple, scalable, and secure way to prove the origin and history of your images, videos, and documents. This helps your users trust that the content they see is authentic, protecting your brand and combating misinformation at its source. ### Attach a Digital Birth Certificate Que allows you to bind a secure, digital "birth certificate" to an asset. We bundle information about an asset's creation and editing history into a tamper-evident data structure called a **manifest**. * **What is it?** A manifest contains assertions (like who created it and what software was used) that are cryptographically **signed**. This signature proves that the information is authentic and hasn't been altered since it was attached. * **Why use it?** In a world of sophisticated AI-generated content, detecting fakes is a losing battle. Que offers a proactive solution: proving what's real. They provide a verifiable chain of **provenance** (the asset's origin and history) that travels with the content wherever it goes. * **How does it work?** Que provides a simple API to create these manifests and sign them to your assets. You define the provenance information, and our service handles the complex cryptography and embeds the credentials directly into the file. You can then verify these credentials later to confirm the asset's history. ### Why Use the Que API? While you could use a low-level C2PA library directly, Que abstracts away the complexity into a managed, cloud-native service. * **Managed & Scalable:** We handle the secure key storage, certificate management, and cryptographic operations in a highly available environment. * **Memory-Efficient:** Our streaming architecture is designed for large files enabling efficient handling of high-resolution video and images. * **Simple Integration:** With our official SDKs and straightforward REST API, you can integrate content provenance into your application in minutes, not weeks. ## LLMs.txt Que provides an LLMs.txt file that helps AI models understand how to interact with your provenance system. You can find it at [https://addque.org/llms-full.txt](https://addque.org/llms-full.txt). # SDKs and Configuration URL: /docs/libraries *** title: SDKs and Configuration description: Install our official libraries and learn how to configure your API key securely. icon: "Code" ------------ import { Tab, Tabs } from '@/components/tabs'; import { Callout } from '@/components/ui/callout'; While you can always interact with Que via direct HTTP requests, our official SDKs provide a more convenient, type-safe, and idiomatic way to integrate Que into your application. ### Official SDKs We provide libraries for most popular languages. To install them, use your standard package manager. ```bash npm install que-sdk ``` ```bash pip install que_media ``` ```bash go get addque.org/sdk/go ``` ```rust // Add to Cargo.toml [dependencies] que-sdk = "0.1.0" ``` ```xml org.addque que 0.0.2 ``` ```bash composer require que/sdk ``` ### Configuring Your API Key The recommended way to configure your API key is through an environment variable. All official Que SDKs are designed to automatically look for the `QUE_API_KEY` variable, making setup simple and secure. Never hardcode your API keys directly in your source code or commit them to version control. Use environment variables or a secret management system. Here is how you would typically initialize the client in each language. If no key is passed to the constructor, the SDK will automatically try to read it from the environment. ```typescript import { Que } from "que-sdk"; // The client automatically reads the QUE_API_KEY_AUTH environment variable. const que = new Que({ apiKeyAuth: process.env["QUE_API_KEY_AUTH"] ?? "", }); // You can also pass it explicitly (not recommended for production). // const que = new Que({ apiKeyAuth: "YOUR_API_KEY" }); ``` ```python import os from que_media import Que # The client automatically reads the QUE_API_KEY_AUTH environment variable. with Que( api_key_auth=os.getenv("QUE_API_KEY_AUTH", ""), ) as que: # Use the client pass # You can also pass it explicitly (not recommended for production). # que = Que(api_key_auth="YOUR_API_KEY") ``` ```go import "addque.org/sdk/go" // The client automatically reads the QUE_API_KEY environment variable. client, err := que.NewClient() // You can also pass it explicitly (not recommended for production). // client, err := que.NewClient(que.WithAPIKey("YOUR_API_KEY")) ``` ```rust use que_sdk::QueClient; // The client automatically reads the QUE_API_KEY environment variable. let client = QueClient::new(); // You can also pass it explicitly (not recommended for production). // let client = QueClient::builder().api_key("YOUR_API_KEY").build(); ``` ```java import org.openapis.openapi.Que; // The client automatically reads the API_KEY_AUTH environment variable. Que sdk = Que.builder() .apiKeyAuth(System.getenv().getOrDefault("API_KEY_AUTH", "")) .build(); // You can also pass it explicitly (not recommended for production). // Que sdk = Que.builder() // .apiKeyAuth("YOUR_API_KEY") // .build(); ``` ```php setSecurity(getenv('QUE_API_KEY_AUTH') ?: '') ->build(); // You can also pass it explicitly (not recommended for production). // $sdk = Que\Que::builder() // ->setSecurity('YOUR_API_KEY') // ->build(); ?> ``` # Quickstart URL: /docs/quickstart *** title: Quickstart description: Sign your first digital asset with Que in under 5 minutes using our SDKs. icon: "Zap" ----------- import { Step, Steps } from 'fumadocs-ui/components/steps'; import { Tab, Tabs } from '@/components/tabs'; import { Callout } from '@/components/ui/callout'; Before you begin, make sure you have: 1. Your **Que API Key** from the developer dashboard. 2. A local image file to sign (e.g., `my-image.jpg`). ### 1. Configure Your API Key For security, it's best to set your API key as an environment variable rather than hardcoding it in your application. ```bash export QUE_API_KEY="YOUR_API_KEY" ``` Our SDKs will automatically detect and use this environment variable. ### 2. Upload Your Asset Upload your local file using the SDK. The SDK handles getting presigned URLs and uploading to S3 automatically. ```typescript import { Que } from "que-sdk"; const que = new Que({ apiKeyAuth: process.env["QUE_API_KEY_AUTH"] ?? "", }); const filePath = "./my-image.jpg"; // Get presigned URL for upload const presignedResult = await que.getPresignedUrl({ filename: filePath.split('/').pop() || "my-image.jpg", }); // Upload file to S3 using the presigned URL // (You'll need to implement the actual file upload using fetch or your preferred HTTP client) import { readFileSync } from 'fs'; const fileBuffer = readFileSync(filePath); const uploadResponse = await fetch(presignedResult.uploadUrl, { method: 'PUT', body: fileBuffer, headers: { 'Content-Type': 'image/jpeg', // Adjust based on your file type }, }); if (!uploadResponse.ok) { throw new Error('Failed to upload file'); } console.log(`Asset uploaded with key: ${presignedResult.key}`); ``` ```python import os from que_media import Que with Que( api_key_auth=os.getenv("QUE_API_KEY_AUTH", ""), ) as que: file_path = "./my-image.jpg" # Get presigned URL for upload presigned_result = que.get_presigned_url(filename="my-image.jpg") # Upload file to S3 using the presigned URL # (You'll need to implement the actual file upload using requests or your preferred HTTP client) import requests with open(file_path, 'rb') as file: upload_response = requests.put( presigned_result.upload_url, data=file, headers={'Content-Type': 'image/jpeg'} ) if upload_response.status_code == 200: print(f"Asset uploaded with key: {presigned_result.key}") else: raise Exception('Failed to upload file') ``` ```go package main import ( "fmt" "addque.org/sdk/go" ) func main() { client, err := que.NewClient() if err != nil { panic(err) } filePath := "./my-image.jpg" // Upload the file - SDK handles presigned URL and upload automatically asset, err := client.Assets.Upload(filePath) if err != nil { panic(err) } fmt.Printf("Asset uploaded with key: %s\n", asset.Key) } ``` ```rust use que_sdk::QueClient; #[tokio::main] async fn main() -> Result<(), Box> { let client = QueClient::new(); let file_path = "./my-image.jpg"; // Upload the file - SDK handles presigned URL and upload automatically let asset = client.assets.upload(file_path).await?; println!("Asset uploaded with key: {}", asset.key); Ok(()) } ``` ```java package hello.world; import java.lang.Exception; import java.io.File; import java.io.FileInputStream; import java.net.http.HttpClient; import java.net.http.HttpRequest; import java.net.http.HttpResponse; import java.net.URI; import org.openapis.openapi.Que; import org.openapis.openapi.models.components.*; import org.openapis.openapi.models.errors.ProblemResponseException; import org.openapis.openapi.models.operations.GetPresignedUrlResponse; class Quickstart { public static void main(String[] args) throws ProblemResponseException, ProblemResponseException, ProblemResponseException, Exception { Que sdk = Que.builder() .apiKeyAuth(System.getenv().getOrDefault("API_KEY_AUTH", "")) .build(); String filePath = "./my-image.jpg"; // Get presigned URL for upload GetPresignedUrlRequest presignReq = GetPresignedUrlRequest.builder() .filename("my-image.jpg") .build(); GetPresignedUrlResponse presignRes = sdk.getPresignedUrl() .request(presignReq) .call(); if (presignRes.presignedUrlResponse().isPresent()) { // Upload file to S3 using the presigned URL File file = new File(filePath); byte[] fileBytes = java.nio.file.Files.readAllBytes(file.toPath()); HttpClient client = HttpClient.newHttpClient(); HttpRequest request = HttpRequest.newBuilder() .uri(URI.create(presignRes.presignedUrlResponse().get().uploadUrl())) .header("Content-Type", "image/jpeg") .PUT(HttpRequest.BodyPublishers.ofByteArray(fileBytes)) .build(); HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString()); if (response.statusCode() == 200) { System.out.println("Asset uploaded with key: " + presignRes.presignedUrlResponse().get().key()); } else { System.out.println("Upload failed: " + response.statusCode()); } } } } ``` ```php setSecurity(getenv('QUE_API_KEY_AUTH') ?: '') ->build(); $filePath = './my-image.jpg'; // Get presigned URL for upload $presignedRequest = new Components\GetPresignedUrlRequest( filename: basename($filePath) ); $presignedResponse = $sdk->getPresignedUrl( request: $presignedRequest ); // Upload file to S3 using the presigned URL $fileContent = file_get_contents($filePath); $context = stream_context_create([ 'http' => [ 'method' => 'PUT', 'header' => 'Content-Type: image/jpeg', 'content' => $fileContent ] ]); $result = file_get_contents($presignedResponse->getPresignedUrlResponse->uploadUrl, false, $context); if ($result !== false) { echo "Asset uploaded with key: " . $presignedResponse->getPresignedUrlResponse->key . "\n"; } ?> ``` ### 3. Sign the Asset Now that the file is uploaded, we can sign it with a manifest containing provenance information. ```typescript // Sign the asset with a manifest const signedAsset = await que.signAsset({ asset: { bucket: "your-bucket", // Replace with your actual bucket key: presignedResult.key, }, manifestJson: JSON.stringify({ title: "My First Signed Image", assertions: [{ label: "stds.schema-org.CreativeWork", data: { "@context": "https://schema.org", "@type": "CreativeWork", "author": [{ "@type": "Person", "name": "Quickstart User" }] } }] }) }); console.log("Signed asset location:", signedAsset.asset.s3Uri); ``` ```python # Sign the asset with a manifest import json manifest_json = json.dumps({ "title": "My First Signed Image", "assertions": [{ "label": "stds.schema-org.CreativeWork", "data": { "@context": "https://schema.org", "@type": "CreativeWork", "author": [{ "@type": "Person", "name": "Quickstart User" }] } }] }) signed_asset = que.sign_asset( asset={ "bucket": "your-bucket", # Replace with your actual bucket "key": presigned_result.key }, manifest_json=manifest_json ) print("Signed asset location:", signed_asset.asset.s3_uri) ``` ```go // Sign the asset with a manifest manifest := que.Manifest{ Title: "My First Signed Image", Assertions: []que.Assertion{ { Label: "stds.schema-org.CreativeWork", Data: map[string]interface{}{ "@context": "https://schema.org", "@type": "CreativeWork", "author": []map[string]string{ {"@type": "Person", "name": "Quickstart User"}, }, }, }, }, } signedAsset, err := client.Assets.Sign(asset.Key, manifest) if err != nil { panic(err) } fmt.Println("Signed asset location:", signedAsset.AssetS3URI) ``` ```rust // Sign the asset with a manifest let manifest = que_sdk::Manifest { title: "My First Signed Image".to_string(), assertions: vec![que_sdk::Assertion { label: "stds.schema-org.CreativeWork".to_string(), data: serde_json::json!({ "@context": "https://schema.org", "@type": "CreativeWork", "author": [{"@type": "Person", "name": "Quickstart User"}] }), }], }; let signed_asset = client.assets.sign(asset.key, manifest).await?; println!("Signed asset location: {}", signed_asset.asset_s3_uri); ``` ```java // Sign the asset with a manifest SignRequest signReq = SignRequest.builder() .asset(AssetRefDto.of(S3.builder() .bucket("your-bucket") // Replace with your actual bucket .key(presignRes.presignedUrlResponse().get().key()) .build())) .manifestJson("{\"title\":\"My First Signed Image\",\"assertions\":[{\"label\":\"stds.schema-org.CreativeWork\",\"data\":{\"@context\":\"https://schema.org\",\"@type\":\"CreativeWork\",\"author\":[{\"@type\":\"Person\",\"name\":\"Quickstart User\"}]}}]}") .build(); SignAssetResponse signRes = sdk.signAsset() .request(signReq) .call(); if (signRes.signResponse().isPresent()) { System.out.println("Signed asset location: " + signRes.signResponse().get().asset().s3Uri()); } ``` ```php getPresignedUrlResponse->key ), manifestJson: json_encode([ 'title' => 'My First Signed Image', 'assertions' => [[ 'label' => 'stds.schema-org.CreativeWork', 'data' => [ '@context' => 'https://schema.org', '@type' => 'CreativeWork', 'author' => [['@type' => 'Person', 'name' => 'Quickstart User']] ] ]] ]) ); $response = $sdk->signAsset( request: $signRequest ); if ($response->signResponse !== null) { echo "Signed asset location: " . $response->signResponse->asset->s3Uri . "\n"; } ?> ``` ### Next Steps Congratulations! You've successfully attached Content Credentials to a digital asset. The signed file now contains a verifiable history of its origin. * Learn more about creating manifests in [Manifests](/docs/core/manifests). * Explore how to check an asset's history in [Verification](/docs/verification). # Signing Assets URL: /docs/signing *** title: Signing Assets description: Learn how to embed C2PA manifests into your digital assets to create verifiable, tamper-evident records of their origin and history using the Que SDK. icon: Pencil ------------ import { Callout } from '@/components/ui/callout'; import { Tab, Tabs } from '@/components/tabs'; ## Overview Signing is the process of cryptographically attaching a C2PA manifest to a digital asset. This action embeds a secure, tamper-evident "birth certificate" and a detailed history directly into the file. It is the fundamental step in creating authentic, verifiable content. This guide is for developers, platforms, and creators who need to prove the authenticity of their digital media. ### What, Why, and How * **What is Signing?** Signing uses the Que service to embed a C2PA manifest—a data structure containing provenance information like author, creation tools, and actions—into an asset. The entire package is then cryptographically signed to prevent undetected modifications. * **Why Sign Assets?** In an environment filled with synthetic and modified media, signing provides a proactive solution. It allows you to protect your brand, establish trust with your audience, and give consumers a reliable way to verify the origin and history of your content. * **How to Get Started** The process involves preparing your asset, defining a manifest, and making a single call to the Que SDK. The quickstart example below shows the simplest way to sign an asset. ## The Signing Workflow The typical workflow for signing an asset with Que is straightforward and designed for security and efficiency, especially with large files. 1. **Define a Manifest**: Construct a JSON object that describes your asset and the actions performed on it. This is the core provenance data you will embed. For a detailed guide, see [Manifests](/docs/core/manifests). 2. **Upload Your Asset**: For security and performance, upload your asset to a location Que can access. The recommended method is using Que's [Presigned Uploads](/docs/core/assets/presigned-upload) for direct, secure S3 uploads. You can also use publicly accessible [URL Inputs](/docs/core/assets/url-inputs), although additional costs are incurred. 3. **Call the `sign` Function**: Use the Que SDK to call the `sign` function, providing the asset's location and your JSON manifest. 4. **Retrieve the Signed Asset**: Que returns the location of the newly signed asset, which is stored securely and ready for distribution. ## Quickstart This example demonstrates the minimum code required to sign an asset. First, ensure your asset is uploaded and you have its S3 key. ```typescript import { Que } from "que-sdk"; const que = new Que({ apiKeyAuth: process.env["QUE_API_KEY_AUTH"] ?? "", }); const asset = { bucket: 'your-asset-bucket', key: 'uploads/photo.jpg', }; const manifest = JSON.stringify({ title: 'Original Photograph', assertions: [ { label: 'stds.schema-org.CreativeWork', data: { '@context': 'https://schema.org', '@type': 'CreativeWork', author: [{ '@type': 'Person', name: 'Jane Photographer' }], }, }, ], }); async function signAsset() { try { const result = await que.signAsset({ asset, manifestJson: manifest, }); console.log('Signing successful:', result); } catch (error) { console.error('Signing failed:', error); } } signAsset(); ``` ```python import os from que_sdk import QueClient, AssetReference que = QueClient(api_key=os.getenv("QUE_API_KEY")) asset = AssetReference( bucket="your-asset-bucket", key="uploads/photo.jpg" ) manifest = """ { "title": "Original Photograph", "assertions": [ { "label": "stds.schema-org.CreativeWork", "data": { "@context": "https://schema.org", "@type": "CreativeWork", "author": [{ "@type": "Person", "name": "Jane Photographer" }] } } ] } """ try: result = que.sign( asset=asset, manifest_json=manifest ) print(f"Signing successful: {result}") except Exception as e: print(f"Signing failed: {e}") ``` ```go package main import ( "context" "fmt" "os" "github.com/que-platform/que-sdk-go" ) func main() { client := quesdk.NewClient(os.Getenv("QUE_API_KEY")) asset := quesdk.AssetReference{ Bucket: "your-asset-bucket", Key: "uploads/photo.jpg", } manifest := `{ "title": "Original Photograph", "assertions": [{ "label": "stds.schema-org.CreativeWork", "data": { "@context": "https://schema.org", "@type": "CreativeWork", "author": [{"@type": "Person", "name": "Jane Photographer"}] } }] }` result, err := client.Sign(context.Background(), &quesdk.SignRequest{ Asset: asset, ManifestJSON: manifest, }) if err != nil { fmt.Printf("Signing failed: %v\n", err) return } fmt.Printf("Signing successful: %+v\n", result) } ``` ```rust // The Que SDK for Rust is not yet available. // The following is a conceptual example. use que_sdk::{QueClient, types::{AssetReference, SignRequest}}; #[tokio::main] async fn main() -> Result<(), Box> { let client = QueClient::new(std::env::var("QUE_API_KEY")?); let asset = AssetReference::S3 { bucket: "your-asset-bucket".to_string(), key: "uploads/photo.jpg".to_string(), }; let manifest = r#"{ "title": "Original Photograph", "assertions": [{ "label": "stds.schema-org.CreativeWork", "data": { "@context": "https://schema.org", "@type": "CreativeWork", "author": [{"@type": "Person", "name": "Jane Photographer"}] } }] }"#.to_string(); let request = SignRequest { asset, manifest_json: manifest, ..Default::default() }; let result = client.sign(request).await?; println!("Signing successful: {:?}", result); Ok(()) } ``` ```java package hello.world; import java.lang.Exception; import org.openapis.openapi.Que; import org.openapis.openapi.models.components.*; import org.openapis.openapi.models.errors.ProblemResponseException; import org.openapis.openapi.models.operations.SignAssetResponse; public class SignAssetExample { public static void main(String[] args) throws ProblemResponseException, ProblemResponseException, ProblemResponseException, Exception { Que sdk = Que.builder() .apiKeyAuth(System.getenv().getOrDefault("API_KEY_AUTH", "")) .build(); SignRequest req = SignRequest.builder() .asset(AssetRefDto.of(S3.builder() .bucket("your-asset-bucket") .key("uploads/photo.jpg") .build())) .manifestJson("{\"title\":\"Original Photograph\",\"assertions\":[{\"label\":\"stds.schema-org.CreativeWork\",\"data\":{\"@context\":\"https://schema.org\",\"@type\":\"CreativeWork\",\"author\":[{\"@type\":\"Person\",\"name\":\"Jane Photographer\"}]}}]}") .build(); SignAssetResponse res = sdk.signAsset() .request(req) .call(); if (res.signResponse().isPresent()) { System.out.println("Signing successful: " + res.signResponse().get().asset().s3Uri()); } } } ``` ```php // The Que SDK for PHP is not yet available. // The following is a conceptual example. getenv('QUE_API_KEY')]); $asset = [ 'bucket' => 'your-asset-bucket', 'key' => 'uploads/photo.jpg' ]; $manifest = json_encode([ 'title' => 'Original Photograph', 'assertions' => [[ 'label' => 'stds.schema-org.CreativeWork', 'data' => [ '@context' => 'https://schema.org', '@type' => 'CreativeWork', 'author' => [['@type' => 'Person', 'name' => 'Jane Photographer']] ] ]] ]); try { $result = $que->sign([ 'asset' => $asset, 'manifest_json' => $manifest ]); print_r($result); } catch (Exception $e) { echo 'Signing failed: ' . $e->getMessage(); } ``` #### Expected Response A successful signing operation returns a JSON object with the location of the signed asset and cryptographic evidence of the signature. ```json { "assurance": "server_measured", "evidence": { "signer": "env_dev", "alg": "ES256" }, "asset_s3_uri": "s3://que-signed-assets/a1b2c3d4/signed-photo.jpg" } ``` ## Configuring the Sign Request Beyond the basics, you can configure the signing process to include enhanced identity claims or set resource limits. ### Creator Identity (CAWG) The Content Authenticity Working Group (CAWG) defines a standard for adding a verifiable creator identity assertion to your manifest. This provides a stronger link between the content and its creator. You can enable it by providing a `cawg` configuration object. For a complete guide, see [Creator Identity (CAWG)](/docs/core/creator-identity-cawg). ### Resource Limits To manage costs and prevent abuse, you can specify processing limits for any signing operation. These control factors like maximum file size and processing timeouts. Que applies sensible default limits to all operations. You only need to override them for special use cases, such as processing exceptionally large files. For more information, see [Limits and Timeouts](/docs/core/limits-and-timeouts). ## Understanding the Response The response object from a successful `sign` call contains three key fields: * `asset_s3_uri`: **The location of your signed asset.** This is an S3 URI pointing to the new file with the C2PA manifest embedded. * `assurance`: The signing method used. `server_measured` indicates that Que's servers streamed and hashed the asset, providing the highest level of assurance. * `evidence`: An object containing cryptographic details about the signature, including the signer ID (`signer`) and the algorithm used (`alg`). ## Best Practices and Error Handling The `manifest_json` parameter must be a valid, stringified JSON object. A common source of errors is passing a malformed string or a native object instead of a string. Always validate your JSON structure before sending the request. If the signing process fails, the SDK will raise an error containing a code and a descriptive message. Common failures include an invalid asset reference, a malformed manifest, or exceeding resource limits. For a full list of possible errors, see [Errors and Troubleshooting](/docs/platform/errors-and-troubleshooting). # Verifying Assets URL: /docs/verification *** title: Verifying Assets description: Learn how to validate C2PA manifests in digital assets to confirm their authenticity, inspect their provenance, and detect tampering using the Que SDK. icon: ShieldCheck ----------------- import { Callout } from '@/components/ui/callout'; import { Tab, Tabs } from '@/components/tabs'; ## Overview Verification is the process of inspecting a digital asset to find, validate, and report on any embedded C2PA manifests. This allows you to confirm an asset's provenance, check for tampering, and make informed decisions about its authenticity. This guide is for developers, platforms, and consumers who need a reliable way to check the history and origin of digital content. ### What, Why, and How * **What is Verification?** Verification analyzes an asset's C2PA data, validates its cryptographic signatures against a managed trust list, and returns a detailed report. It answers the question: "Is this asset authentic, and where did it come from?" * **Why Verify Assets?** In a world where content can be easily manipulated, verification provides a crucial layer of trust. It helps you programmatically identify authentic content, detect unauthorized modifications, and display provenance information to your users, empowering them to evaluate content critically. * **How to Get Started** The process involves providing a reference to an asset and calling a single `verify` function in the Que SDK. The quickstart example below shows the simplest way to get a trust summary for an asset. ## Quickstart This example demonstrates the minimum code required to verify an asset. The default verification mode (`summary`) provides a high-level trust status. ```typescript import { Que } from "que-sdk"; const que = new Que({ apiKeyAuth: process.env["QUE_API_KEY_AUTH"] ?? "", }); const asset = { bucket: 'que-signed-assets', key: 'a1b2c3d4/signed-photo.jpg', }; async function verifyAsset() { try { const result = await que.verifyAsset({ asset, includeCertificates: true, }); const report = JSON.parse(result.report); console.log('Verification successful. Report:', report); } catch (error) { console.error('Verification failed:', error); } } verifyAsset(); ``` ```python import os import json from que_media import Que with Que( api_key_auth=os.getenv("QUE_API_KEY_AUTH", ""), ) as que: try: result = que.verify_asset(asset={ "bucket": "que-signed-assets", "key": "a1b2c3d4/signed-photo.jpg" }, mode="summary", include_certificates=True) report = json.loads(result.report) print(f"Verification successful. Report: {report}") except Exception as e: print(f"Verification failed: {e}") ``` ```go package main import ( "context" "encoding/json" "fmt" "os" "github.com/que-platform/que-sdk-go" ) func main() { client := quesdk.NewClient(os.Getenv("QUE_API_KEY")) asset := quesdk.AssetReference{ Bucket: "que-signed-assets", Key: "a1b2c3d4/signed-photo.jpg", } result, err := client.Verify(context.Background(), &quesdk.VerifyRequest{ Asset: asset, }) if err != nil { fmt.Printf("Verification failed: %v\n", err) return } var report map[string]interface{} json.Unmarshal([]byte(result.Report), &report) fmt.Printf("Verification successful. Report: %+v\n", report) } ``` ```rust // The Que SDK for Rust is not yet available. // The following is a conceptual example. use que_sdk::{QueClient, types::{AssetReference, VerifyRequest}}; #[tokio::main] async fn main() -> Result<(), Box> { let client = QueClient::new(std::env::var("QUE_API_KEY")?); let asset = AssetReference::S3 { bucket: "que-signed-assets".to_string(), key: "a1b2c3d4/signed-photo.jpg".to_string(), }; let request = VerifyRequest { asset, ..Default::default() }; let result = client.verify(request).await?; let report: serde_json::Value = serde_json::from_str(&result.report)?; println!("Verification successful. Report: {}", report); Ok(()) } ``` ```java package hello.world; import java.lang.Exception; import org.openapis.openapi.Que; import org.openapis.openapi.models.components.*; import org.openapis.openapi.models.errors.ProblemResponseException; import org.openapis.openapi.models.operations.VerifyAssetResponse; public class VerifyAssetExample { public static void main(String[] args) throws ProblemResponseException, ProblemResponseException, ProblemResponseException, Exception { Que sdk = Que.builder() .apiKeyAuth(System.getenv().getOrDefault("API_KEY_AUTH", "")) .build(); VerifyRequest req = VerifyRequest.builder() .asset(AssetRefDto.of(S3.builder() .bucket("que-signed-assets") .key("a1b2c3d4/signed-photo.jpg") .build())) .includeCertificates(true) .build(); VerifyAssetResponse res = sdk.verifyAsset() .request(req) .call(); if (res.verifyResponse().isPresent()) { System.out.println("Verification successful. Report: " + res.verifyResponse().get().report()); } } } ``` ```php setSecurity(getenv('QUE_API_KEY_AUTH') ?: '') ->build(); $verifyRequest = new Components\VerifyRequest( asset: new Components\S3( bucket: 'que-signed-assets', key: 'a1b2c3d4/signed-photo.jpg' ), mode: Components\VerifyRequestMode::Detailed ); try { $response = $sdk->verifyAsset( request: $verifyRequest ); if ($response->verifyResponse !== null) { $report = json_decode($response->verifyResponse->report); print_r($report); } } catch (Exception $e) { echo 'Verification failed: ' . $e->getMessage(); } ``` #### Expected Response A successful verification returns a JSON object containing a `report` field. This field is a **JSON string** that must be parsed to access the report details. ```json { "report": "{\"validationStatus\":[{\"code\":\"claim.signature.validated\",\"explanation\":\"Claim signature validated\"}],\"summary\":{\"isTrusted\":true,\"signer\":\"CN=Que Signing Service\"}}" } ``` ## Configuring the Verification Request You can customize the verification process to control the level of detail in the report and how different types of manifests are handled. ### Verification Modes Que offers several modes to control the depth of the verification report. * `summary` (Default): Returns a high-level pass/fail result, trust status, and basic signer information. This is the fastest option for simple validation. * `info`: Provides basic information about the manifest, its claims, and signing entities. * `detailed`: A comprehensive, forensic-level report of all assertions, claims, signatures, and validation steps. * `tree`: Renders a hierarchical view of the manifest's ingredient relationships, showing the full provenance chain. ### Remote Manifests Some assets may contain references to manifests stored externally rather than embedded in the file. By default, Que does not fetch these for security reasons. You can enable this behavior if needed. Enabling remote manifest fetching can expose your application to security risks like Server-Side Request Forgery (SSRF) if the URLs are malicious. Only enable this feature if you trust the source of your assets. For more details, see our guide on [Remote Manifests](/docs/core/remote-manifests). ### Creator Identity (CAWG) During verification, you can configure how Que handles CAWG identity assertions, such as making a valid creator identity mandatory for the verification to pass. See [Creator Identity (CAWG)](/docs/core/creator-identity-cawg) for configuration options. ## Understanding the Verification Report The `report` field in the response is a JSON string that your application must parse. The structure of the parsed object depends on the `mode` you requested. A `summary` report contains two key fields: * `validationStatus`: An array of codes indicating which validation steps passed or failed. A `claim.signature.validated` code confirms the cryptographic signature is intact. * `summary`: An object containing the most important high-level result. * `isTrusted`: A boolean that is `true` if the asset was signed by a certificate in Que's managed Trust List. This is the primary indicator of authenticity. * `signer`: A string identifying the entity that signed the asset. A `false` value for `isTrusted` does not necessarily mean the asset is malicious. It could mean it was signed by a self-signed certificate or a certificate from an unknown authority. The detailed codes in the report provide the context needed to make an informed decision. For a full list of possible errors and report codes, see [Errors and Troubleshooting](/docs/platform/errors-and-troubleshooting). # Sign an asset with a C2PA manifest URL: /docs/api/signAsset *** title: Sign an asset with a C2PA manifest full: true \_openapi: method: POST route: /v1/sign toc: \[] structuredData: headings: \[] contents: * content: > Embeds a C2PA manifest into a digital asset and signs it using a server-side cryptographic key. The asset is processed using memory-efficient streaming to temporary storage before signing. This operation cryptographically links the asset to its provenance information, creating an immutable record of the asset's origin, authorship, and any processing history. *** {/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */} Embeds a C2PA manifest into a digital asset and signs it using a server-side cryptographic key. The asset is processed using memory-efficient streaming to temporary storage before signing. This operation cryptographically links the asset to its provenance information, creating an immutable record of the asset's origin, authorship, and any processing history. # Verify the C2PA manifest of an asset URL: /docs/api/verifyAsset *** title: Verify the C2PA manifest of an asset full: true \_openapi: method: POST route: /v1/verify toc: \[] structuredData: headings: \[] contents: * content: > Analyzes a digital asset to find, validate, and report on any embedded C2PA manifests. This allows you to confirm the asset's provenance, authenticity, and processing history. The asset is processed using memory-efficient streaming to temporary storage during verification. Returns detailed validation results including trust status, signer information, and any validation failures. *** {/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */} Analyzes a digital asset to find, validate, and report on any embedded C2PA manifests. This allows you to confirm the asset's provenance, authenticity, and processing history. The asset is processed using memory-efficient streaming to temporary storage during verification. Returns detailed validation results including trust status, signer information, and any validation failures. # Core Concepts URL: /docs/core *** title: Core Concepts description: Decision-focused guides for building with Que Cloud. Learn what to choose, when to use it, and how to wire it up. icon: Layers ------------ import { Card, Cards } from 'fumadocs-ui/components/card'; * Why and when you’d use the feature * What decisions you need to make * How to configure requests (with links to the API reference) * Common pitfalls # Limits & Timeouts URL: /docs/core/limits-and-timeouts *** title: Limits & Timeouts description: Configure per-request processing limits to manage costs, prevent abuse, and ensure reliable performance with large assets. --------------------------------------------------------------------------------------------------------------------------------------- import { Callout } from '@/components/ui/callout'; To ensure stability and prevent resource exhaustion, the QueCloud API operates with default limits on file sizes and processing time. For most use cases, these defaults are sufficient. However, for applications that handle very large files (e.g., high-resolution video), you may need to override these limits on a per-request basis. **Why configure limits?** * **Cost Control:** Prevent unexpectedly large files from causing high processing and storage costs. * **Reliability:** Set appropriate timeouts for assets fetched from slow external URLs. * **Security:** Protect against denial-of-service attacks that use excessively large files to exhaust resources. ## Overriding Default Limits You can provide a `limits` object in your `sign` or `verify` requests. Any values you provide will override the system defaults for that specific request. ```json { "asset": { "bucket": "your-bucket", "key": "large-video.mp4" }, "manifest_json": "...", "limits": { "max_asset_size_bytes": 536870912, "stream_timeout_ms": 120000 } } ``` ## Configurable Parameters * `max_asset_size_bytes`: The maximum allowed size of the source asset in bytes. The request will fail if the file is larger than this value. * `max_output_size_bytes`: The maximum allowed size of the *signed* asset. This prevents manifests from unexpectedly bloating the file size. * `max_stream_copy_bytes`: A limit on the amount of data buffered in memory during processing. * `stream_timeout_ms`: The timeout in milliseconds for downloading an asset, particularly relevant when using **[URL Inputs](/docs/core/assets/url-inputs)** from a slow source. It's best practice to set the tightest limits possible for your use case. For example, if your application only handles user profile pictures under 5MB, set `max_asset_size_bytes` to `5242880` to reject unexpectedly large uploads early. # Remote Manifests URL: /docs/core/remote-manifests *** title: Remote Manifests description: Allow the verification process to fetch and validate C2PA manifests that are hosted externally via a URL. ---------------------------------------------------------------------------------------------------------------------- import { Callout } from '@/components/ui/callout'; While manifests are typically embedded directly within an asset, the C2PA specification allows for them to be hosted externally and linked from the asset's metadata. This is known as a remote manifest store. **Why use remote manifests?** * **Large Manifests:** Can offload very large or complex provenance histories from the asset itself. * **Dynamic Updates:** Allows for certain types of updates without modifying the original asset file (though this is an advanced and rare use case). By default, the QueCloud API **does not** attempt to fetch remote manifests for security and performance reasons. You must explicitly enable this behavior on a per-request basis. ## Enabling Remote Manifest Fetching To allow the verifier to fetch remote manifests, set `allow_remote_manifests` to `true` in your `/v1/verify` request body. ```json { "asset": { "bucket": "your-bucket", "key": "asset-with-remote-manifest.jpg" }, "allow_remote_manifests": true } ``` ## Security Considerations * **`allow_insecure_remote_http`**: By default, the service will only fetch manifests from secure `https` URLs. If you must fetch from an insecure `http` URL, you can set `allow_insecure_remote_http` to `true`, but this is **strongly discouraged** as it exposes the process to man-in-the-middle attacks. Only enable remote manifest fetching when you have a specific need and trust the sources where manifests may be hosted. For most use cases, relying on embedded manifests is simpler and more secure. # Errors & Troubleshooting URL: /docs/platform/errors-and-troubleshooting *** title: Errors & Troubleshooting description: How to identify and resolve common errors in the Que API. ---------------------------------------------------------------------- import { Callout } from '@/components/ui/callout'; ## Overview The Que API provides detailed error responses following the [RFC 7807 Problem Details format](https://datatracker.ietf.org/doc/html/rfc7807). This makes it easy to identify what went wrong and how to fix it. Every error response contains: * **title** – human-readable summary of the problem * **status** – HTTP status code * **code** – machine-usable error keyword * **detail** – specific explanation * **details** *(optional)* – structured context (e.g., retry wait time) You should always log the full error response to help with debugging. *** ## Common Errors ### 400 — Bad Request Your request was malformed or missing required fields. ```json { "type": "about:blank", "title": "bad_request", "status": 400, "code": "bad_request", "detail": "manifest_json is required when mode=server_measure" } ``` Check your request body against the schema. Ensure required fields are provided and JSON is valid. *** ### 401 — Unauthorized The request did not include a valid API key. ```json { "title": "unauthorized", "status": 401, "code": "unauthorized", "detail": "invalid API key" } ``` **Solution:** Pass your API key in the `x-api-key` header. *** ### 403 — Forbidden Your API key lacks permission. ```json { "title": "forbidden", "status": 403, "detail": "you do not have permission to perform this action" } ``` **Solution:** Verify you’re using the correct environment key and role. *** ### 422 — Unprocessable Entity The asset or manifest could not be processed. ```json { "title": "verification failed", "status": 422, "code": "engine_verification", "detail": "verification failed" } ``` Most often caused by **unsupported file formats** or invalid C2PA manifests. Check that the asset format is [supported](/docs/platform/supported-formats). *** ### 429 — Too Many Requests You’ve exceeded your rate limit. ```json { "title": "rate_limited", "status": 429, "detail": "try again in 5000 ms", "details": { "try_again_in_ms": 5000 } } ``` **Solution:** Implement exponential backoff and respect the `Retry-After` or `details.try_again_in_ms` field. *** ### 500 / 503 / 504 — Server Errors Error inside the Que service, or upstream timeout. These errors are temporary. Retry the request with backoff. If persistent, contact support. *** ## Debugging Checklist 1. Confirm your request matches the API schema. 2. Verify your **API key** is valid and active. 3. Check if the asset is a **supported format**. 4. Review error `code` for specific hints. 5. Respect retries and rate limits. 6. For persistent issues, gather request/response logs and file a support ticket. # Rate Limits & Quotas URL: /docs/platform/rate-limits-and-quotas *** title: Rate Limits & Quotas description: Understand Que API request limits and how to manage them. ---------------------------------------------------------------------- import { Callout } from '@/components/ui/callout'; ## Overview Que applies default **rate limits** to all API keys.\ Rate limits ensure the service remains stable and fair for all teams using the platform. Each request you send (to **sign**, **verify**, or **upload** assets) counts toward your key’s quota. Most projects never hit these limits, but heavy usage workloads may need adjustments. If your application needs higher sustained throughput, contact support. *** ## Default Limits By default: * **Per-key quotas** are applied over short windows (seconds to minutes). * If your key exceeds the quota, requests will fail with a **429 Too Many Requests** error. * The error response may include a `Retry-After` header indicating when you can safely retry. ### Example 429 Response ```json { "type": "about:blank", "title": "rate_limited", "status": 429, "code": "rate_limited", "detail": "try again in 5000 ms", "details": { "try_again_in_ms": 5000 } } ``` *** ## Best Practices * **Batch smaller assets when possible.** Each upload, verify, or sign call is counted separately. * **Implement retry logic.** Use the `Retry-After` value to decide when to retry. * **Monitor usage.** Use metrics in the developer dashboard or your own logging to observe request volume. Flooding retries without respecting the `Retry-After` header will keep your client blocked and may extend cool-down times. *** ## Future Quotas While Que is in free beta, limits are meant as **fair use caps**, not billing triggers.\ In the future, rate-limit tiers will align with pricing plans: * **Starter**: light usage, generous free quota * **Pro**: higher sustained throughput for commercial use * **Enterprise**: custom SLAs and throughput guarantees We’ll update this page # Security & Key Management URL: /docs/platform/security-and-key-management *** title: Security & Key Management description: Protect your API keys and apply best security practices when using Que. ------------------------------------------------------------------------------------ import { Callout } from '@/components/ui/callout'; ## Overview All Que API requests (except `/healthz`) require an API key provided in the `x-api-key` header. API keys grant access to provenance operations like signing and verifying assets. Protect them as you would any other secret. *** ## Best Practices ### 1. Store Secrets Securely * Use environment variables (`ENV_VAR`) or dedicated secret managers (AWS Secrets Manager, HashiCorp Vault). * Never commit keys to GitHub or config files. ```bash # Example in Linux export QUE_API_KEY="your-key-here" ``` ```typescript // Example in TypeScript import { Que } from "que-sdk"; const que = new Que({ apiKeyAuth: process.env["QUE_API_KEY_AUTH"] ?? "", }); // The SDK automatically handles API key authentication const result = await que.verifyAsset({ asset: { url: "https://example.com/asset.jpg" }, includeCertificates: true, }); ``` *** ### 2. Use Least Privilege Request only the API key permissions you need for your use case. Do not reuse production keys in development or CI. *** ### 3. Rotate Keys Regularly Generate new keys periodically and revoke old ones. This limits the window of exposure if a key leaks. *** ### 4. Avoid Client-Side Exposure Never embed raw API keys in browser or mobile apps. Instead, proxy requests through your backend. *** ### 5. Apply Network Security Controls * Use HTTPS at all times. * Restrict outbound requests to known Que hosts. * Monitor API usage via your [usage dashboard](/docs/platform/usage-tracking-and-billing). *** ## Example: Passing an API Key ```python import os from que_media import Que with Que( api_key_auth=os.getenv("QUE_API_KEY_AUTH", ""), ) as que: result = que.verify_asset(asset={ "url": "https://example.com/asset.jpg" }, mode="summary", include_certificates=True) import json report = json.loads(result.report) print(report) ``` Run all tests with sandbox keys first. Switch to production keys only when you’re confident. # Supported File Formats URL: /docs/platform/supported-formats *** title: Supported File Formats description: File types Que can process for signing and verification. --------------------------------------------------------------------- import { Callout } from '@/components/ui/callout'; ## Overview The Que platform supports a wide range of media file formats for signing and verification. Support is driven by the underlying C2PA Rust library and is consistent across SDKs unless noted otherwise. Some file formats may not work reliably in the beta release. If you run into issues, confirm the format is supported below and report the error. *** ## Supported Formats | Extensions | MIME Types | | ------------- | ------------------------------------------------------------------------------ | | **avi** | `video/msvideo`, `video/x-msvideo`, `video/avi`, `application/x-troff-msvideo` | | **avif** | `image/avif` | | **c2pa** | `application/x-c2pa-manifest-store` | | **dng** | `image/x-adobe-dng` | | **gif** | `image/gif` | | **heic** | `image/heic` | | **heif** | `image/heif` | | **jpg, jpeg** | `image/jpeg` | | **m4a** | `audio/mp4` | | **mp3** | `audio/mpeg` | | **mp4** | `video/mp4`, `application/mp4` \* | | **mov** | `video/quicktime` | | **pdf** | `application/pdf` \*\* | | **png** | `image/png` | | **svg** | `image/svg+xml` | | **tif, tiff** | `image/tiff` | | **wav** | `audio/wav` | | **webp** | `image/webp` | *** ### Notes * `*` **Fragmented MP4 (DASH)** is supported only for **file-based operations** from the Rust library. * `**` **PDF** is supported in **read-only mode** (verification only). *** ## Troubleshooting Format Issues If you see `engine_c2pa` or `Unsupported manifest format` errors: 1. Confirm the file extension and MIME type are valid. 2. Ensure the file isn’t corrupted or encrypted. 3. Check this page for support status. 4. Report unsupported edge cases to support. When in doubt, try verifying the asset first. Unsupported file formats fail early with a clear error. # Usage Tracking & Billing URL: /docs/platform/usage-tracking-and-billing *** title: Usage Tracking & Billing description: Learn how Que measures usage and how future billing will work. --------------------------------------------------------------------------- import { Callout } from '@/components/ui/callout'; import { Tab, Tabs } from '@/components/tabs'; ## Overview Que tracks your API usage automatically for every authenticated request.\ This helps us monitor system health and will enable **billing by usage** once paid plans launch. During the beta, **Que is free for all participants**. You can experiment without being charged. *** ## What We Track Every request made with your API key is logged for: * **Sign requests** – embedding provenance manifests into your assets * **Verify requests** – validating provenance and trust chains * **Utility calls** – such as presigned uploads and trust list refreshes Internal metrics include: * **Total request volume** * **Byte sizes for streamed assets** * **Errors and retry counts** *** ## Billing Model (Coming Soon) When Que moves out of beta: * **Core billing unit**: request counts for **sign** and **verify** endpoints * Additional charges may apply for **large assets** that exceed normal limits * **Enterprise pricing** will be available for organizations requiring custom throughput, retention, and support Pricing will be **very low cost** relative to other provenance services. Subscription options and enterprise-level agreements will be announced soon. *** ## Current Beta Policy * **Free during beta:** No invoices are generated. * **Not production-ready:** Keys, quotas, and access may change as features evolve. * **Support available:** Please reach out if you spot issues or need quota exceptions. *** ## Example: Tracking Request Volume Although you don’t need to handle billing yet, you can implement usage tracking in your own app logs. For example: ```typescript import { Que } from "que-sdk"; const que = new Que({ apiKeyAuth: process.env["QUE_API_KEY_AUTH"] ?? "", }); async function logRequest(operation: string, success: boolean) { console.log(`[Que] ${operation} ${success ? 'succeeded' : 'failed'}`); } // Using the Que SDK try { const result = await que.verifyAsset({ asset: { url: "https://example.com/photo.jpg" }, includeCertificates: true, }); logRequest("verifyAsset", true); } catch (error) { logRequest("verifyAsset", false); } ``` ```python import os from que_media import Que def log_request(operation, success): print(f"[Que] {operation} {'succeeded' if success else 'failed'}") with Que( api_key_auth=os.getenv("QUE_API_KEY_AUTH", ""), ) as que: try: result = que.verify_asset(asset={ "url": "https://example.com/photo.jpg" }, mode="summary", include_certificates=True) log_request("verify_asset", True) except Exception as e: log_request("verify_asset", False) ``` This helps you get visibility now, before billing launches. *** ## Next Steps 1. Use Que freely during beta testing. 2. Track your usage internally. 3. Plan for per-request pricing once Que announces its official model. # Get an S3 presigned URL for secure uploads URL: /docs/api/asset-management/getPresignedUrl *** title: Get an S3 presigned URL for secure uploads full: true \_openapi: method: POST route: /v1/assets/presign toc: \[] structuredData: headings: \[] contents: * content: > Generates a temporary, cryptographically signed URL that allows secure direct upload of assets to S3 without exposing AWS credentials. This is the recommended approach for uploading assets, especially large files, as it: * Avoids sending large payloads through the API server * Provides secure, time-limited access to S3 * Enables resumable uploads for better reliability * Reduces API server memory usage and network overhead Use the returned URL to make a PUT request with your asset file, then use the returned key for subsequent sign/verify operations. *** {/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */} Generates a temporary, cryptographically signed URL that allows secure direct upload of assets to S3 without exposing AWS credentials. This is the recommended approach for uploading assets, especially large files, as it: * Avoids sending large payloads through the API server * Provides secure, time-limited access to S3 * Enables resumable uploads for better reliability * Reduces API server memory usage and network overhead Use the returned URL to make a PUT request with your asset file, then use the returned key for subsequent sign/verify operations. # Service Health Check URL: /docs/api/utility/getHealthCheck *** title: Service Health Check full: true \_openapi: method: GET route: /healthz toc: \[] structuredData: headings: \[] contents: * content: >- Performs a basic health check of the API service. This endpoint does not require authentication and is typically used by monitoring systems. *** {/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */} Performs a basic health check of the API service. This endpoint does not require authentication and is typically used by monitoring systems. # Retrieve the current C2PA trust bundle URL: /docs/api/utility/getTrustList *** title: Retrieve the current C2PA trust bundle full: true \_openapi: method: GET route: /v1/trust-list toc: \[] structuredData: headings: \[] contents: * content: > Fetches the latest C2PA trust list containing trusted certificate authorities, hardware manufacturers, and trust policies. The trust list is used during manifest verification to: * Validate signer certificates against trusted Certificate Authorities * Verify hardware manufacturer claims for camera-captured content * Apply trust policies for different validation scenarios Trust lists are versioned and should be refreshed periodically as new trusted entities are added or certificates expire. *** {/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */} Fetches the latest C2PA trust list containing trusted certificate authorities, hardware manufacturers, and trust policies. The trust list is used during manifest verification to: * Validate signer certificates against trusted Certificate Authorities * Verify hardware manufacturer claims for camera-captured content * Apply trust policies for different validation scenarios Trust lists are versioned and should be refreshed periodically as new trusted entities are added or certificates expire. # Assets in Que URL: /docs/core/assets *** title: Assets in Que description: Learn how to reference digital assets for signing and verification, whether they are uploaded directly or hosted on a remote server. ------------------------------------------------------------------------------------------------------------------------------------------------- ## Overview In the Que ecosystem, an **Asset** is the digital file—like an image, video, or document—that you want to sign or verify. Before Que can process a file, it needs to know where to find it. You provide this information using an **Asset Reference**, a simple object that points to the asset's location. This system is designed for security and efficiency. Instead of sending large, multi-megabyte files in API requests, you provide a lightweight pointer, and Que's infrastructure handles the heavy lifting of fetching and processing the file in a memory-efficient way. ## Asset Reference Methods Que supports two primary ways to reference an asset. Your choice depends on where your asset is located and your application's architecture. ## Best Practices for Handling Assets * **Provide Content-Type**: When possible, explicitly state the asset's MIME type (e.g., `image/jpeg`). While Que can often detect the type automatically, providing it explicitly is more reliable and efficient. * **Respect Size Limits**: Que is designed to handle large files, but there are configurable limits to prevent resource exhaustion. Be aware of the [default limits](/docs/core/limits-and-timeouts) and override them if you are processing exceptionally large assets. * **Handle Errors**: If Que cannot access or process an asset from the reference you provide, the SDK will raise an error. Ensure your application handles these cases gracefully, such as a 404 Not Found for a broken URL. See [Errors and Troubleshooting](/docs/platform/errors-and-troubleshooting) for more details. # Presigned Uploads URL: /docs/core/assets/presigned-upload *** title: Presigned Uploads description: Use presigned URLs for a secure and efficient way to upload assets directly from your application to Que for processing. ------------------------------------------------------------------------------------------------------------------------------------- import { Tab, Tabs } from '@/components/tabs'; import { Callout } from '@/components/ui/callout'; import { Step, Steps } from 'fumadocs-ui/components/steps'; ## Overview A presigned upload URL is a secure, temporary link that grants your application write-only permission to upload a file directly to a specific location in cloud storage (S3). This is the **recommended method** for getting assets into the Que system, especially from client-side applications like web browsers or mobile apps. ### What, Why, and How * **What is a Presigned Upload?** It's a two-step process: first, you ask the Que SDK for a special upload URL. Second, your application uses that URL to upload the file directly to S3. * **Why use Presigned Uploads?** * **Security**: Your API keys and cloud credentials are never exposed to the client. The URL is temporary and restricted to a single upload. * **Performance**: Large files bypass your server and go straight to S3, reducing load and bandwidth on your infrastructure. * **Scalability**: It leverages the highly scalable and reliable infrastructure of S3 for file uploads. * **How does it work?** The workflow involves three simple steps: request a URL, upload the file, and then use the returned object key to reference the asset in a sign or verify call. ## The Upload Workflow ### Request a Presigned URL First, call the SDK's function to generate a presigned URL. This tells Que you intend to upload a file. ```typescript import { Que } from "que-sdk"; const que = new Que({ apiKeyAuth: process.env["QUE_API_KEY_AUTH"] ?? "", }); async function getUploadUrl() { try { const presignResponse = await que.getPresignedUrl({ filename: "my-asset.jpg", }); console.log('Received presigned URL:', presignResponse.uploadUrl); console.log('Use this key for processing:', presignResponse.key); return presignResponse; } catch (error) { console.error('Failed to get presigned URL:', error); } } getUploadUrl(); ``` ```python import os from que_media import Que with Que( api_key_auth=os.getenv("QUE_API_KEY_AUTH", ""), ) as que: try: response = que.get_presigned_url(filename="my-asset.jpg") print(f"Received presigned URL: {response.upload_url}") print(f"Use this key for processing: {response.key}") except Exception as e: print(f"Failed to get presigned URL: {e}") ``` ```go package main import ( "context" "fmt" "os" "github.com/que-platform/que-sdk-go" ) func main() { client := quesdk.NewClient(os.Getenv("QUE_API_KEY")) resp, err := client.GetPresignedURL(context.Background()) if err != nil { fmt.Printf("Failed to get presigned URL: %v\n", err) return } fmt.Printf("Received presigned URL: %s\n", resp.URL) fmt.Printf("Use this key for processing: %s\n", resp.Key) } ``` ```rust // The Que SDK for Rust is not yet available. // The following is a conceptual example. use que_sdk::QueClient; #[tokio::main] async fn main() -> Result<(), Box> { let client = QueClient::new(std::env::var("QUE_API_KEY")?); let response = client.get_presigned_url().await?; println!("Received presigned URL: {}", response.url); println!("Use this key for processing: {}", response.key); Ok(()) } ``` ```java package hello.world; import java.lang.Exception; import org.openapis.openapi.Que; import org.openapis.openapi.models.components.*; import org.openapis.openapi.models.errors.ProblemResponseException; import org.openapis.openapi.models.operations.GetPresignedUrlResponse; public class PresignExample { public static void main(String[] args) throws ProblemResponseException, ProblemResponseException, ProblemResponseException, Exception { Que sdk = Que.builder() .apiKeyAuth(System.getenv().getOrDefault("API_KEY_AUTH", "")) .build(); GetPresignedUrlRequest req = GetPresignedUrlRequest.builder() .filename("my-asset.jpg") .build(); GetPresignedUrlResponse res = sdk.getPresignedUrl() .request(req) .call(); if (res.presignedUrlResponse().isPresent()) { System.out.println("Received presigned URL: " + res.presignedUrlResponse().get().uploadUrl()); System.out.println("Use this key for processing: " + res.presignedUrlResponse().get().key()); } } } ``` ```php setSecurity(getenv('QUE_API_KEY_AUTH') ?: '') ->build(); try { $request = new Components\GetPresignedUrlRequest( filename: 'my-asset.jpg' ); $response = $sdk->getPresignedUrl( request: $request ); if ($response->getPresignedUrlResponse !== null) { echo "Received presigned URL: " . $response->getPresignedUrlResponse->uploadUrl . "\n"; echo "Use this key for processing: " . $response->getPresignedUrlResponse->key . "\n"; } } catch (Exception $e) { echo 'Failed: ' . $e->getMessage(); } ``` #### Response Breakdown The response object contains two critical pieces of information: * `url`: The temporary, secure URL to which you will upload your file. * `key`: The unique identifier (S3 object key) for your file once it's uploaded. **You must save this key.** ### Upload the Asset File Using the `url` from the previous step, make an HTTP `PUT` request with the raw file data as the request body. It is crucial to set the `Content-Type` header in your `PUT` request to match the MIME type of the file you are uploading (e.g., `image/jpeg`, `video/mp4`). ```typescript // Upload file using the presigned URL async function uploadFile(presignedUrl: string, file: File) { try { const response = await fetch(presignedUrl, { method: 'PUT', headers: { 'Content-Type': file.type, // e.g., 'image/jpeg' }, body: file, }); if (response.ok) { console.log('File uploaded successfully'); } else { console.error('Upload failed:', response.statusText); } } catch (error) { console.error('Upload error:', error); } } // Usage const file = document.getElementById('fileInput').files[0]; uploadFile(presignResponse.url, file); ``` ```python import requests def upload_file(presigned_url: str, file_path: str, content_type: str): try: with open(file_path, 'rb') as file: response = requests.put( presigned_url, data=file, headers={'Content-Type': content_type} ) if response.status_code == 200: print("File uploaded successfully") else: print(f"Upload failed: {response.status_code}") except Exception as e: print(f"Upload error: {e}") # Usage upload_file(presign_response.url, "path/to/your/file.jpg", "image/jpeg") ``` ```go package main import ( "bytes" "fmt" "io" "mime/multipart" "net/http" "os" ) func uploadFile(presignedURL, filePath, contentType string) error { file, err := os.Open(filePath) if err != nil { return err } defer file.Close() var buf bytes.Buffer writer := multipart.NewWriter(&buf) writer.SetBoundary("") part, err := writer.CreateFormFile("file", filePath) if err != nil { return err } _, err = io.Copy(part, file) if err != nil { return err } writer.Close() req, err := http.NewRequest("PUT", presignedURL, &buf) if err != nil { return err } req.Header.Set("Content-Type", contentType) client := &http.Client{} resp, err := client.Do(req) if err != nil { return err } defer resp.Body.Close() if resp.StatusCode == 200 { fmt.Println("File uploaded successfully") } else { fmt.Printf("Upload failed: %d\n", resp.StatusCode) } return nil } // Usage err := uploadFile(presignResponse.URL, "path/to/your/file.jpg", "image/jpeg") if err != nil { fmt.Printf("Error: %v\n", err) } ``` ```rust use reqwest; use std::fs::File; use std::io::Read; async fn upload_file(presigned_url: &str, file_path: &str, content_type: &str) -> Result<(), Box> { let mut file = File::open(file_path)?; let mut buffer = Vec::new(); file.read_to_end(&mut buffer)?; let client = reqwest::Client::new(); let response = client .put(presigned_url) .header("Content-Type", content_type) .body(buffer) .send() .await?; if response.status().is_success() { println!("File uploaded successfully"); } else { println!("Upload failed: {}", response.status()); } Ok(()) } // Usage #[tokio::main] async fn main() -> Result<(), Box> { upload_file(&presign_response.url, "path/to/your/file.jpg", "image/jpeg").await?; Ok(()) } ``` ```java import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.net.http.HttpClient; import java.net.http.HttpRequest; import java.net.http.HttpResponse; import java.net.URI; import java.nio.file.Files; public class FileUploader { public static void uploadFile(String presignedUrl, String filePath, String contentType) { try { File file = new File(filePath); byte[] fileBytes = Files.readAllBytes(file.toPath()); HttpClient client = HttpClient.newHttpClient(); HttpRequest request = HttpRequest.newBuilder() .uri(URI.create(presignedUrl)) .header("Content-Type", contentType) .PUT(HttpRequest.BodyPublishers.ofByteArray(fileBytes)) .build(); HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString()); if (response.statusCode() == 200) { System.out.println("File uploaded successfully"); } else { System.out.println("Upload failed: " + response.statusCode()); } } catch (IOException | InterruptedException e) { System.err.println("Upload error: " + e.getMessage()); } } // Usage public static void main(String[] args) { uploadFile(presignResponse.getUrl(), "path/to/your/file.jpg", "image/jpeg"); } } ``` ```php setSecurity(getenv('QUE_API_KEY_AUTH') ?: '') ->build(); function uploadFile(string $presignedUrl, string $filePath, string $contentType): void { $fileContent = file_get_contents($filePath); $context = stream_context_create([ 'http' => [ 'method' => 'PUT', 'header' => "Content-Type: $contentType\r\n", 'content' => $fileContent ] ]); $result = file_get_contents($presignedUrl, false, $context); if ($result !== false) { echo "File uploaded successfully\n"; } else { echo "Upload failed\n"; } } // Usage $presignedRequest = new Components\GetPresignedUrlRequest( filename: 'my-asset.jpg' ); $presignedResponse = $sdk->getPresignedUrl( request: $presignedRequest ); if ($presignedResponse->getPresignedUrlResponse !== null) { uploadFile($presignedResponse->getPresignedUrlResponse->uploadUrl, 'path/to/your/file.jpg', 'image/jpeg'); } ?> ``` ### Use the Asset Key for Processing After the upload is complete, the asset is ready for processing. Use the `key` returned in Step 1 to construct an Asset Reference for a [signing](/docs/signing) or [verification](/docs/verification) call. The S3 bucket will be your organization's designated upload bucket. ```typescript // Example of using the key in a subsequent call const result = await que.signAsset({ asset: { bucket: 'your-asset-upload-bucket', key: presignResponse.key, // The key from Step 1 }, manifestJson: JSON.stringify({ title: "My Signed Asset", assertions: [{ label: "stds.schema-org.CreativeWork", data: { "@context": "https://schema.org", "@type": "CreativeWork", "author": [{ "@type": "Person", "name": "Creator" }] } }] }), }); ``` # URL Inputs URL: /docs/core/assets/url-inputs *** title: URL Inputs description: Reference assets directly from a public HTTP or HTTPS URL for processing, ideal for server-side workflows or publicly hosted content. -------------------------------------------------------------------------------------------------------------------------------------------------- import { Tab, Tabs } from '@/components/tabs'; import { Callout } from '@/components/ui/callout'; ## Overview In addition to direct uploads, Que can process assets that are already available on the web. You can provide a direct, publicly accessible HTTP or HTTPS URL as an **Asset Reference**. Que will then stream the file from that URL for signing or verification. This method is best suited for server-to-server workflows where you are processing content that is already hosted, such as images from a CMS or files from a public archive. This feature allows Que's servers to access external URLs. For security, this capability may be disabled by default in your environment. * **Server-Side Request Forgery (SSRF) Risk**: If user-provided URLs are not validated, an attacker could attempt to make Que's servers access internal or malicious resources. * **HTTPS Recommended**: Always use HTTPS URLs to ensure the integrity and confidentiality of the asset during transit. ## Usage Example To use a URL input, create an Asset Reference object that contains a `url` field instead of a `bucket` and `key`. ```typescript import { Que } from "que-sdk"; const que = new Que({ apiKeyAuth: process.env["QUE_API_KEY_AUTH"] ?? "", }); const asset = { url: 'https://example.com/assets/photo-to-verify.jpg', }; async function verifyUrlAsset() { try { const result = await que.verifyAsset({ asset, includeCertificates: true, }); console.log('Verification successful:', JSON.parse(result.report)); } catch (error) { console.error('Verification failed:', error); } } verifyUrlAsset(); ``` ```python import os from que_media import Que with Que( api_key_auth=os.getenv("QUE_API_KEY_AUTH", ""), ) as que: try: result = que.verify_asset(asset={ "url": "https://example.com/assets/photo-to-verify.jpg" }, mode="summary", include_certificates=True) import json report = json.loads(result.report) print("Verification successful") except Exception as e: print(f"Verification failed: {e}") ``` ```go package main import ( "context" "fmt" "os" "github.comcom/que-platform/que-sdk-go" ) func main() { client := quesdk.NewClient(os.Getenv("QUE_API_KEY")) asset := quesdk.AssetReference{ URL: "https://example.com/assets/photo-to-verify.jpg", } _, err := client.Verify(context.Background(), &quesdk.VerifyRequest{ Asset: asset, }) if err != nil { fmt.Printf("Verification failed: %v\n", err) return } fmt.Println("Verification successful") } ``` ```rust // The Que SDK for Rust is not yet available. // The following is a conceptual example. use que_sdk::{QueClient, types::{AssetReference, VerifyRequest}}; #[tokio::main] async fn main() -> Result<(), Box> { let client = QueClient::new(std::env::var("QUE_API_KEY")?); let asset = AssetReference::Url { url: "https://example.com/assets/photo-to-verify.jpg".to_string(), }; let request = VerifyRequest { asset, ..Default::default() }; client.verify(request).await?; println!("Verification successful"); Ok(()) } ``` ```java package hello.world; import java.lang.Exception; import org.openapis.openapi.Que; import org.openapis.openapi.models.components.*; import org.openapis.openapi.models.errors.ProblemResponseException; import org.openapis.openapi.models.operations.VerifyAssetResponse; public class UrlInputExample { public static void main(String[] args) throws ProblemResponseException, ProblemResponseException, ProblemResponseException, Exception { Que sdk = Que.builder() .apiKeyAuth(System.getenv().getOrDefault("API_KEY_AUTH", "")) .build(); VerifyRequest req = VerifyRequest.builder() .asset(AssetRefDto.of(PresignedUrl.builder() .url("https://example.com/assets/photo-to-verify.jpg") .build())) .includeCertificates(true) .build(); VerifyAssetResponse res = sdk.verifyAsset() .request(req) .call(); if (res.verifyResponse().isPresent()) { System.out.println("Verification successful."); } } } ``` ```php setSecurity(getenv('QUE_API_KEY_AUTH') ?: '') ->build(); $verifyRequest = new Components\VerifyRequest( asset: new Components\PresignedUrl( url: 'https://example.com/assets/photo-to-verify.jpg' ), mode: Components\VerifyRequestMode::Detailed ); try { $response = $sdk->verifyAsset( request: $verifyRequest ); if ($response->verifyResponse !== null) { echo "Verification successful.\n"; } } catch (Exception $e) { echo 'Verification failed: ' . $e->getMessage(); } ``` ## Best Practices * **Validate URLs**: Before passing a URL to Que, especially if it originates from a user, validate it on your backend to ensure it points to a legitimate, expected domain and resource. * **Use HTTPS**: To protect against man-in-the-middle attacks, only use `https://` URLs. * **Check for Errors**: Be prepared to handle errors related to network failures, timeouts, or invalid URLs (e.g., 404 Not Found, 503 Service Unavailable). For a full list, see [Errors and Troubleshooting](/docs/platform/errors-and-troubleshooting). * **Prefer Presigned Uploads for Client-Side Workflows**: For security and performance reasons, do not use URL inputs for files originating from a user's device. Always use the [Presigned Uploads](/docs/core/assets/presigned-upload) workflow for that scenario. # Manifest Assertions & Actions URL: /docs/core/manifests/assertions *** title: Manifest Assertions & Actions description: Understand how to define provenance details and asset history using Que Manifest Assertions and Actions. --------------------------------------------------------------------------------------------------------------------- ## Overview Manifest **Assertions** and **Actions** are core components of Que Manifests, providing the granular detail about an asset's provenance. Assertions are structured metadata entries that describe *when*, *where*, and *how* an asset was created or transformed, or details about its content. Actions, a specific type of assertion, detail the sequence of events and modifications applied to an asset throughout its lifecycle. Together, they allow you to build a comprehensive, verifiable history for any digital asset, crucial for maintaining trust and transparency in its origin and evolution. ## Assertions Assertions are key-value pairs embedded within a Que Manifest. They declare specific pieces of information about the asset, its history, or its content. In the JSON manifest structure that Que uses, each assertion is represented by a `ManifestAssertion` object. These objects are typically placed within an `assertions` array. A `ManifestAssertion` requires a `label` (a string identifier) and `data` (arbitrary information in JSON-LD format). Optional properties like `kind` and `instance` offer further control. The standard form of an assertion in a JSON manifest looks like this: ```json { "label": "Label string", "data": { // Arbitrary data, often in JSON-LD format }, "kind": "Json" // Optional: "Cbor", "Json", "Binary", or "Uri" // "instance" : 0 // Optional and rarely used } ``` ### Metadata Assertions Metadata assertions provide standardized ways to embed descriptive or technical information about an asset. They must include one or more `@context` properties in the `data` object, as specified by [JSON-LD](https://www.w3.org/TR/json-ld/#the-context). #### Creative Work Assertion (`stds.schema-org.CreativeWork`) * **What is it?** This assertion states that an asset is the product of creative effort, such as an original photograph or artwork. It leverages [Schema.org's `CreativeWork`](https://schema.org/CreativeWork) type to describe properties like author, publisher, and publication date. * **Why use it?** It provides fundamental attribution and contextual information about the asset's creation, enhancing its discoverability and establishing its creative origin. * **How to get started:** Include this assertion with relevant details about the creative work. ```json { "label": "stds.schema-org.CreativeWork", "data": { "@context": "https://schema.org", "@type": "CreativeWork", "url": "https://example.com/my-original-photo" }, "kind": "Json" } ``` #### Exif Assertion (`stds.exif`) * **What is it?** This assertion allows you to embed [Exchangeable Image File (Exif)](https://www.cipa.jp/std/documents/download_e.html?DC-008-Translation-2019-E) metadata within the manifest. Exif includes technical details captured by digital cameras, like make, model, shutter speed, ISO, and GPS location. * **Why use it?** It cryptographically links verifiable camera and device information to the asset, providing concrete evidence of its capture details. * **How to get started:** Use the `stds.exif` label and populate the `data` field with Exif properties. ```json { "label": "stds.exif", "data": { "@context": { "exif": "http://ns.adobe.com/exif/1.0/" }, "exif:GPSVersionID": "2.2.0.0", "exif:GPSLatitude": "39,21.102N", "exif:GPSLongitude": "74,26.5737W", "exif:GPSTimeStamp": "2019-09-22T18:22:57Z" } } ``` #### IPTC Metadata Assertion (`stds.iptc`) * **What is it?** This assertion incorporates properties from the [IPTC Photo Metadata Standard](https://www.iptc.org/std/photometadata/specification/IPTC-PhotoMetadata) and [Video Metadata Standard](https://www.iptc.org/standards/video-metadata-hub/recommendation/). It describes ownership, rights, and rich descriptive metadata for images and videos. * **Why use it?** It allows you to embed widely recognized and standardized professional metadata, ensuring comprehensive and portable information about the asset's content, rights, and usage. * **How to get started:** Use the `stds.iptc` label with JSON-LD formatted data adhering to IPTC standards. Avoid using the IPTC `plus:DataMining` property. Instead, use the Que ["Do Not Train" assertion](#do-not-train-assertion) for explicit data mining and AI/ML training permissions. ```json { "label": "stds.iptc", "data": { "@context": { "Iptc4xmpCore": "http://iptc.org/std/Iptc4xmpCore/1.0/xmlns/", "dc": "http://purl.org/dc/elements/1.1/", "photoshop": "http://ns.adobe.com/photoshop/1.0/" }, "photoshop:DateCreated": "Aug 31, 2022", "dc:creator": ["Julie Smith"], "dc:rights": "Copyright (C) 2022 Example. All Rights Reserved.", "photoshop:Credit": "Julie Smith/Example Photo" } } ``` ### Que Standard Assertions The C2PA Technical Specification defines a set of standard assertions that Que integrates and supports. In addition, you can define [custom assertions](#custom-assertions) for specific application needs. Here are some of the most important standard assertions you'll use with Que: | Assertion | Label | Description | | :---------------------------------------- | :--------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------- | | [Actions](#actions) | `c2pa.actions` | Records creation, edits, and other modifications applied to an asset, such as cropping, color adjustments, or generative AI transformations. | | ["Do Not Train"](#do-not-train-assertion) | `c2pa.training-mining` | Indicates permissions regarding the use of an asset for data mining, AI/ML training, or inference. | | [Creative Work](#creative-work-assertion) | `stds.schema-org.CreativeWork` | Identifies the asset as a product of creative effort, providing authorship and context. | | [Exif Information](#exif-assertion) | `stds.exif` | Embeds camera and device metadata, linking technical capture details to the asset. | | [Content Bindings](#content-bindings) | `c2pa.hash.*`, `c2pa.soft-binding` | Cryptographically links portions of an asset to its assertions, ensuring integrity and preventing tampering. | | [IPTC Metadata](#iptc-metadata-assertion) | `stds.iptc` | Provides professional metadata describing ownership, rights, and descriptive information for image and video assets. | ### Do Not Train Assertion (`c2pa.training-mining`) * **What is it?** Assertions with the `c2pa.training-mining` label define whether permission is granted to use an asset for data mining, machine learning (ML) training, or inference (sometimes referred to as "do not infer"). * **Why use it?** This is crucial for creators and rights holders to explicitly control how their content is used in emerging AI and data processing contexts. * **How to get started:** Specify entries within the `data` object for different types of use, each with a `use` property. The `use` property can have one of three values: * `allowed`: Permission is granted for this type of use. * `notAllowed`: Permission is NOT granted for this type of use. * `constrained`: Permission is not unconditionally granted. You can provide more details (e.g., contact information) in the `constraints_info` text property. For more information, see the [C2PA Technical Specification on Training and Data Mining](https://c2pa.org/specifications/specifications/1.4/specs/C2PA_Specification.html#_training_and_data_mining). ```json { "label": "c2pa.training-mining", "data": { "entries": { "c2pa.ai_generative_training": { "use": "notAllowed" }, "c2pa.ai_inference": { "use": "notAllowed" }, "c2pa.ai_training": { "use": "notAllowed" }, "c2pa.data_mining": { "use": "constrained", "constraint_info": "Contact legal@example.com for more info." } } } } ``` ### Content Bindings (`c2pa.hash.*`) * **What is it?** Content bindings are standard assertions, such as `c2pa.hash.boxes` and `c2pa.hash.data`, that cryptographically identify specific portions of an asset. They ensure that the assertions are immutably linked to the content they describe. * **Why use it?** These bindings are fundamental to verifying the integrity of the asset and its provenance. If the content changes in a way not accounted for by the manifest, the bindings will fail verification. * **How to get started:** While Que automatically manages many content bindings during signing, understanding their structure is important for advanced verification and debugging. The `exclusions` property, for example, denotes parts of the asset intentionally excluded from the hash calculation (like the manifest itself). For more information on content bindings, see the [C2PA Technical Specification](https://c2pa.org/specifications/specifications/1.4/specs/C2PA_Specification.html#_binding_to_content). ```json { "label": "c2pa.hash.data", "data": { "alg": "sha256", "exclusions": [ { "length": 51179, "start": 20 } ], "hash": "DcGR4k9M6aLXXCeDii4tSdX45rrIM5HSr1Wy/czQ6ro=", "name": "jumbf manifest", "pad": "" } } ``` ### Custom Assertions * **What are they?** When standard assertions do not cover your specific use cases, Que allows you to define custom assertions. * **Why use them?** They provide the flexibility to embed unique, application-specific data into the manifest, ensuring that all relevant provenance information can be recorded. * **How to get started:** A custom assertion uses a label string with [reverse domain name notation](https://en.wikipedia.org/wiki/Reverse_domain_name_notation) syntax (e.g., `com.mycompany.myproduct`). ```json { "label": "com.mycompany.myproduct", "data": { "git_hash": "023bb51", "lib_name": "Que Integration Library", "lib_version": "1.0.0", "target_spec_version": "1.2" }, "kind": "Json" } ``` ## Actions Actions are a specialized type of assertion that specifically record events related to an asset's creation, edits, or other significant modifications. They are expressed as an array of objects within a `c2pa.actions` assertion. * **What are they?** Actions provide a chronological record of what happened to an asset, by whom, and with what tools. * **Why use them?** They build a transparent and verifiable history, which is critical for establishing authenticity and understanding the transformations an asset has undergone. * **How to get started:** Include an `actions` array within the `data` of a `c2pa.actions` assertion, with each object detailing a specific event. ```json { "label": "c2pa.actions", "data": { "actions": [ { "action": "c2pa.created", "digitalSourceType": "http://cv.iptc.org/newscodes/digitalsourcetype/trainedAlgorithmicMedia", "softwareAgent": "Que AI Tool" } ] } } ``` Each object in the `actions` array has the following standard properties: | Property | Required? | Description | Example | | :------------------ | :-------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :-------------------------------------------------------------- | | `action` | Yes | The specific action performed (e.g., `c2pa.created`, `c2pa.cropped`). See [Action Names](#action-names). | `c2pa.created` | | `digitalSourceType` | No | A URL identifying how an asset was created or modified, using an [IPTC term](https://cv.iptc.org/newscodes/digitalsourcetype/). See [Digital Source Type](#digital-source-type). | `http://cv.iptc.org/newscodes/digitalsourcetype/digitalCapture` | | `softwareAgent` | No | The software or hardware (tool, service, or device) used to perform the action. | `"Que Editor"` | | `parameters` | No | Additional, action-specific information. This is often used to link actions to [ingredients](#linking-to-ingredients-instance_id). | `{"ingredientIds": ["instance-id-123"]}` | ### Action Names The `action` property's value must be one of the pre-defined [standard C2PA action strings](https://c2pa.org/specifications/specifications/1.4/specs/C2PA_Specification.html#_actions) (e.g., `c2pa.created`, `c2pa.cropped`, `c2pa.resized`). You can also define custom action names using reverse domain name notation. The set of standard C2PA actions includes fundamental events like `c2pa.created` for when an asset is initially generated, and various others for content modifications. ### Digital Source Type * **What is it?** The `digitalSourceType` property specifies how an asset was created or modified (e.g., "digital capture," "digitized from negative," or "trained algorithmic media"). * **Why use it?** It provides critical context about the method of creation, helping to classify and understand the nature of the asset (e.g., whether it's an original photo or AI-generated). * **How to get started:** Use a URL from the International Press Telecommunications Council (IPTC) [NewsCodes Digital Source Type scheme](https://cv.iptc.org/newscodes/digitalsourcetype/). The value is typically of the form `http://cv.iptc.org/newscodes/digitalsourcetype/`, where `` is one of the types below: | Code | Description | | :------------------------ | :--------------------------------------------------------------------------------------------------------------- | | `algorithmicallyEnhanced` | Minor augmentation or correction by algorithm. | | `algorithmicMedia` | Media created purely by an algorithm not based on any sampled training data. | | `composite` | Mix or composite of several elements, any of which may or may not be generative AI. | | `compositeCapture` | Mix or composite of several elements that are all captures of real life. | | `compositeSynthetic` | Mix or composite of several elements, at least one of which is synthetic. | | `digitalCreation` | Media created by a human using non-generative tools. | | `digitalCapture` | Digital media captured from a real-life source using a digital camera or recording device. | | `humanEdits` | Augmentation, correction or enhancement by one or more humans using non-generative tools. | | `screenCapture` | A capture of the contents of a computer or mobile device screen. | | `trainedAlgorithmicMedia` | Digital media created algorithmically using a model derived from sampled content (e.g., by generative AI tools). | | `virtualRecording` | Live recording of a virtual event based on synthetic and optionally captured elements. | This table provides a summary. For the authoritative and most up-to-date list, always refer to the [IPTC NewsCodes Digital Source Type scheme](https://cv.iptc.org/newscodes/digitalsourcetype/) (controlled vocabulary). ### Generative AI Action To specify that an asset was created or significantly modified using generative AI, use the `c2pa.created` action with a `digitalSourceType` that indicates algorithmic media. * `trainedAlgorithmicMedia`: For an asset created entirely by generative AI tools. * `compositeWithTrainedAlgorithmicMedia`: For an asset that incorporates one or more elements created by generative AI tools (e.g., inpainting, outpainting). ```json { "label": "c2pa.actions", "data": { "actions": [ { "action": "c2pa.created", "digitalSourceType": "http://cv.iptc.org/newscodes/digitalsourcetype/trainedAlgorithmicMedia", "softwareAgent": "Que Generative Service" } ] } } ``` Where `"Que Generative Service"` is the name of the generative AI tool or service used. ### Parameters * **What are they?** The `parameters` property within an action object can contain any data that provides more details on the action. * **Why use them?** They allow for highly specific contextual information to be attached to an action, enhancing the granularity of the provenance record. * **How to get started:** Add an object to the `parameters` property with your custom data. A common use case is to link actions to [ingredients](/docs/core/manifests/ingredients) using `ingredientIds`. ```json { "action": "c2pa.color_adjustments", "parameters": { "com.example.color_profile": "sRGB IEC61966-2.1", "com.example.brightness_value": "26" } } ``` The C2PA specification requires that certain actions (such as `c2pa.transcoded`, `c2pa.repackaged`, `c2pa.opened`, or `c2pa.placed`) must have one or more associated [ingredients](/docs/core/manifests/ingredients). This is achieved by including the `ingredientIds` parameter within the `parameters` object, containing an array of `instance_id` values from the relevant ingredients. ### Linking to Ingredients (`instance_id`) When an action involves other assets (like an image being composited onto another), these are referred to as [ingredients](/docs/core/manifests/ingredients). The `instance_id` property uniquely identifies an ingredient within a manifest. To associate an action with an ingredient, the `parameters` object for that action must include an `ingredientIds` property, which is an array containing the `instance_id` values of the ingredients involved. ```json { "ingredients": [ { "title": "background.jpeg", "format": "image/jpeg", "instance_id": "xmp.iid:813ee422-9736-4cdc-9be6-4e35ed8e41cb", "relationship": "parentOf" } ], "assertions": [ { "label": "c2pa.actions", "data": { "actions": [ { "action": "c2pa.placed", "parameters": { "ingredientIds": [ "xmp.iid:813ee422-9736-4cdc-9be6-4e35ed8e41cb" ] } } ] } } ] } ``` In this example, the `c2pa.placed` action is explicitly linked to the ingredient identified by `"xmp.iid:813ee422-9736-4cdc-9be6-4e35ed8e41cb"`, showing that `background.jpeg` was incorporated into the asset. # CAWG Identity Assertions URL: /docs/core/manifests/cawg *** title: CAWG Identity Assertions description: Use CAWG identity assertions to prove creator roles in C2PA assets ------------------------------------------------------------------------------- import { Callout } from '@/components/ui/callout'; import { Tab, Tabs } from '@/components/tabs'; ## Overview **CAWG identity assertions** let you prove control over a digital identity and record a creator’s role in an asset’s lifecycle.\ You can embed these assertions in a C2PA manifest during asset signing, or validate them during verification. Identity assertions are useful when you want to: * **Organizations**: Prove authorship using X.509 certificates tied to trusted entities (e.g. publishers, media companies). * **Individuals**: Prove authorship using verified signals from identity providers (e.g. websites, social accounts, affiliations). Que can validate all CAWG identity assertions. Signing is only supported when using X.509 certificates. *** ## Quickstart Here’s the minimal example to add a CAWG identity assertion during signing: ```typescript import { Que } from "que-sdk"; const que = new Que({ apiKeyAuth: process.env["QUE_API_KEY_AUTH"] ?? "", }); const result = await que.signAsset({ asset: { bucket: "que-assets-dev", key: "uploads/photo.jpg", }, manifestJson: JSON.stringify({ title: "Original Photo", assertions: [{ label: "cawg.identity", data: { "@context": [ "https://www.w3.org/ns/credentials/v2", "https://cawg.io/identity/1.1/ica/context/" ], "type": ["VerifiableCredential", "IdentityClaimsAggregationCredential"], "issuer": "did:web:trusted.idbroker.com", "validFrom": "2025-04-29T17:34:44Z", "verifiedIdentities": [{ "type": "cawg.social_media", "username": "xyz", "uri": "https://www.instagram.com/xyz", "verifiedAt": "2024-10-08T18:04:08Z", "provider": { "id": "https://instagram.com", "name": "instagram" } }] } }] }) }); console.log(result.asset.s3Uri); ``` ```python import os import json from que_media import Que with Que( api_key_auth=os.getenv("QUE_API_KEY_AUTH", ""), ) as que: res = que.sign_asset(asset={ "bucket": "que-assets-dev", "key": "uploads/photo.jpg" }, manifest_json=json.dumps({ "title": "Original Photo", "assertions": [{ "label": "cawg.identity", "data": { "@context": [ "https://www.w3.org/ns/credentials/v2", "https://cawg.io/identity/1.1/ica/context/" ], "type": ["VerifiableCredential", "IdentityClaimsAggregationCredential"], "issuer": "did:web:trusted.idbroker.com", "validFrom": "2025-04-29T17:34:44Z", "verifiedIdentities": [{ "type": "cawg.social_media", "username": "xyz", "uri": "https://www.instagram.com/xyz", "verifiedAt": "2024-10-08T18:04:08Z", "provider": { "id": "https://instagram.com", "name": "instagram" } }] } }] })) print(res.asset.s3_uri) ``` ```go client := que.NewClient(os.Getenv("QUE_API_KEY")) res, err := client.SignAsset(que.SignRequest{ Asset: que.Asset{Bucket: "que-assets-dev", Key: "uploads/photo.jpg"}, Mode: "server_measure", ManifestJson: `{"title":"Original Photo"}`, Cawg: &que.CawgIdentityDto{ Signer: que.Signer{Type: "use_main_signer"}, SigningAlg: "ed25519", ReferencedAssertions: []string{"c2pa.actions"}, }, }) if err != nil { panic(err) } fmt.Println(res.AssetS3Uri) ``` ```rust use que::{Client, SignRequest, Asset}; use std::env; #[tokio::main] async fn main() { let client = Client::new(env::var("QUE_API_KEY").unwrap()); let res = client.sign_asset(SignRequest { asset: Asset { bucket: "que-assets-dev".into(), key: "uploads/photo.jpg".into(), }, mode: "server_measure".into(), manifest_json: r#"{"title":"Original Photo"}"#.into(), cawg: Some(que::CawgIdentityDto { signer: que::Signer::UseMainSigner, signing_alg: "ed25519".into(), referenced_assertions: vec!["c2pa.actions".into()], }), ..Default::default() }).await.unwrap(); println!("{}", res.asset_s3_uri.unwrap()); } ``` ```java package hello.world; import java.lang.Exception; import org.openapis.openapi.Que; import org.openapis.openapi.models.components.*; import org.openapis.openapi.models.errors.ProblemResponseException; import org.openapis.openapi.models.operations.SignAssetResponse; public class CawgExample { public static void main(String[] args) throws ProblemResponseException, ProblemResponseException, ProblemResponseException, Exception { Que sdk = Que.builder() .apiKeyAuth(System.getenv().getOrDefault("API_KEY_AUTH", "")) .build(); SignRequest req = SignRequest.builder() .asset(AssetRefDto.of(S3.builder() .bucket("que-assets-dev") .key("uploads/photo.jpg") .build())) .manifestJson("{\"title\":\"Original Photo\",\"assertions\":[{\"label\":\"cawg.identity\",\"data\":{\"@context\":[\"https://www.w3.org/ns/credentials/v2\",\"https://cawg.io/identity/1.1/ica/context/\"],\"type\":[\"VerifiableCredential\",\"IdentityClaimsAggregationCredential\"],\"issuer\":\"did:web:trusted.idbroker.com\",\"validFrom\":\"2025-04-29T17:34:44Z\",\"verifiedIdentities\":[{\"type\":\"cawg.social_media\",\"username\":\"xyz\",\"uri\":\"https://www.instagram.com/xyz\",\"verifiedAt\":\"2024-10-08T18:04:08Z\",\"provider\":{\"id\":\"https://instagram.com\",\"name\":\"instagram\"}}]}}]}") .build(); SignAssetResponse res = sdk.signAsset() .request(req) .call(); if (res.signResponse().isPresent()) { System.out.println(res.signResponse().get().asset().s3Uri()); } } } ``` ```php setSecurity(getenv('QUE_API_KEY_AUTH') ?: '') ->build(); $signRequest = new Components\SignRequest( asset: new Components\S3( bucket: 'que-assets-dev', key: 'uploads/photo.jpg' ), manifestJson: json_encode([ 'title' => 'Original Photo', 'assertions' => [[ 'label' => 'cawg.identity', 'data' => [ '@context' => [ 'https://www.w3.org/ns/credentials/v2', 'https://cawg.io/identity/1.1/ica/context/' ], 'type' => ['VerifiableCredential', 'IdentityClaimsAggregationCredential'], 'issuer' => 'did:web:trusted.idbroker.com', 'validFrom' => '2025-04-29T17:34:44Z', 'verifiedIdentities' => [[ 'type' => 'cawg.social_media', 'username' => 'xyz', 'uri' => 'https://www.instagram.com/xyz', 'verifiedAt' => '2024-10-08T18:04:08Z', 'provider' => [ 'id' => 'https://instagram.com', 'name' => 'instagram' ] ]] ] ]] ]) ); $response = $sdk->signAsset( request: $signRequest ); if ($response->signResponse !== null) { echo $response->signResponse->asset->s3Uri; } ?> ``` *** ## Identity Assertion Types You can add CAWG identity information in two main ways: ### 1. X.509 Certificate Assertions * Prove identity using a **cryptographic certificate**. * Enterprise-friendly (e.g. newsrooms, publishers). * Can be **validated and signed** by Que. ### 2. Aggregated Identity Claims * Collect identity signals from trusted providers (websites, social accounts, ID checks). * Good for **individual creators**. * Que can **validate** these claims. Signing not supported. *** ## Example JSON Payloads ### X.509 Certificate Assertion ```json "cawg": { "signer": { "type": "use_main_signer" }, "signing_alg": "ed25519", "referenced_assertions": ["c2pa.actions"], "timestamper": "digicert" } ``` ### Identity Claim Aggregator ```json "assertions": [ { "label": "cawg.identity", "data": { "@context": [ "https://www.w3.org/ns/credentials/v2", "https://cawg.io/identity/1.1/ica/context/" ], "type": ["VerifiableCredential", "IdentityClaimsAggregationCredential"], "issuer": "did:web:trusted.idbroker.com", "validFrom": "2025-04-29T17:34:44Z", "verifiedIdentities": [ { "type": "cawg.social_media", "username": "xyz", "uri": "https://www.instagram.com/xyz", "verifiedAt": "2024-10-08T18:04:08Z", "provider": { "id": "https://instagram.com", "name": "instagram" } } ] } } ] ``` *** ## Verified Identity Types The `verifiedIdentities[].type` can be one of: | Value | Meaning | | ---------------------------- | --------------------------------------------------------- | | `cawg.document_verification` | Verified with government-issued documents | | `cawg.web_site` | Ownership of a website or domain | | `cawg.affiliation` | Proof of organizational membership or employment | | `cawg.social_media` | Verified control of a social account | | `cawg.crypto_wallet` | Verified control of a crypto wallet or blockchain account | *** ## Next Steps * Learn how to [Sign Assets with Que](/docs/signing) * See how to [Verify and Validate Identity Assertions](/docs/verification) # Manifest Examples URL: /docs/core/manifests/examples *** title: Manifest Examples description: Examples of manifests and validation reports. ---------------------------------------------------------- ## Simple Manifest This is a minimal manifest containing a basic claim. ```json { "title": "Original Photo", "format": "image/jpeg", "assertions": [ { "label": "stds.schema-org.CreativeWork", "data": { "@context": "https://schema.org", "@type": "CreativeWork", "author": [{ "@type": "Person", "name": "Jane Doe" }] } } ] } ``` *** ## Detailed Manifest A detailed manifest is longer and contains JUMBF references, signatures, and hash values. Use detailed mode only if you need low-level internals. For most workflows, the simplified form is enough. ```json { "title": "Signed Document", "format": "application/pdf", "assertions": [...], "manifests": { "urn:uuid:abc-123": { "c2pa.assertions": { "hash": "sha256-...", "claim": "..." }, "c2pa.signature": { "issuer": "CN=Que Signing Service", "valid": true } } } } ``` # Introduction URL: /docs/core/manifests *** title: Introduction description: Understand the core concept of Que Manifests for digital asset provenance. --------------------------------------------------------------------------------------- import { Callout } from '@/components/ui/callout'; ## Overview Que Manifests are the fundamental building blocks for establishing and verifying the provenance of digital assets within the Que platform. They serve as a cryptographically verifiable record of an asset's origin, authorship, and processing history. By attaching manifests, Que ensures that the authenticity and integrity of digital content can be reliably tracked and validated, offering transparency in an increasingly complex digital landscape. ## How Manifests Are Stored and Retrieved A collection of manifests, known as a *manifest store*, is securely linked to a digital asset. This manifest store contains crucial provenance information. When an asset is created or modified using a Que-compatible tool or service, new manifests are added to its store, extending its verifiable history. Que can discover and process a manifest store linked to an asset in three primary ways: 1. **Directly Embedded**: The manifest data is included within the asset's metadata itself. This is the most common method for many image and video formats. 2. **Sidecar File**: The manifest is stored in a separate file located alongside the asset. This file shares the same base name as the asset but uses a `.c2pa` extension (e.g., `myphoto.jpg` would have `myphoto.c2pa`). 3. **Remote URL**: The asset's metadata contains a reference (URL) pointing to an external manifest store hosted on a network. This allows for dynamic and potentially large manifest stores to be managed independently of the asset file size. An asset can contain both an embedded/sidecar manifest store and a reference to a remote manifest store, combining local and network-based provenance. You can configure Que to [allow fetching remote manifests](/docs/core/remote-manifests). To determine if an asset has Que provenance, the Que service checks for the presence of a manifest store in the order listed above. This ensures comprehensive coverage and reliable discovery of provenance data. The most-recently added manifest in a store is considered the *active manifest*. This active manifest is cryptographically bound to the asset, typically by a hash, ensuring its validity and direct connection to the content. ## Binary vs. JSON Manifests The underlying C2PA specification defines manifests as a binary structure, often encapsulated in the JPEG universal metadata box format ([JUMBF](https://www.iso.org/standard/84635.html)). This binary format is highly efficient for embedding within assets but can be complex and challenging for direct human interaction or programmatic generation. Que simplifies this by providing a declarative **JSON manifest structure**. This human-readable format acts as an abstract translation layer, making it much easier to understand, create, and manage manifest content. You describe the desired provenance information using a clear JSON syntax, and the Que API or SDK handles the conversion into the appropriate binary C2PA format for embedding. This approach allows developers to focus on defining the asset's history without grappling with low-level binary complexities. ## Cryptographic Time-stamps ### What are Time-stamps? A time-stamp in the context of C2PA is a cryptographic proof that a particular piece of data (specifically, the C2PA claim data structure within a manifest) existed at a certain date and time. It functions much like a digital notary service. This proof is typically provided by a trusted third-party Time-Stamp Authority (TSA) that adheres to standards like [Internet X.509 Public Key Infrastructure Time-Stamp Protocol (RFC 3161)](https://datatracker.ietf.org/doc/html/rfc3161). ### Why use Time-stamps? Time-stamps are critical for maintaining the long-term validity and trustworthiness of signed manifests. Without a trusted time-stamp, a manifest's signature can become invalid when the associated signing credential (e.g., a digital certificate) expires or is revoked. By including a time-stamp, you establish an independently verifiable and auditable record of when the signature was applied, preserving its integrity even if the original certificate's validity period ends. Manifests signed without trusted time-stamps will cease to be cryptographically valid when the signing credential expires or is revoked. Always strive to include time-stamps in your signing workflows for maximum assurance. ### How to use Time-stamps When signing an asset with Que, you can configure the use of a time-stamping service. The Que API will integrate with a compliant TSA to embed this proof into your manifest. For programmatic signing via the Que API, the `timestamper` property within the [`CawgIdentityDto`](/docs/core/creator-identity-cawg#cawg-identity-dto) schema (for CAWG identity assertions) or equivalent configuration for main signing allows you to specify a TSA, either by a predefined service ("digicert") or a custom URL. The time-stamp then appears in the `evidence` object of the [`SignResponse`](/docs/api/signAsset#signresponse) when reading the manifest store. ## Further Reading Explore related concepts to deepen your understanding of Que's provenance features: * [Que Assets: How Que handles digital assets](/docs/core/assets) * [Signing Assets with Que](/docs/signing) * [Verifying Assets with Que](/docs/verification) * [Managing Remote Manifests](/docs/core/remote-manifests) * [C2PA Specification](https://spec.c2pa.org/specifications/): The foundational technical specification for Content Authenticity Initiative. * [JUMBF (ISO/IEC 19566-5)](https://www.iso.org/standard/84635.html): A framework for JPEG standards to add universal metadata. # Ingredients URL: /docs/core/manifests/ingredients *** title: Ingredients description: Describe component assets that make up a composed digital asset. ----------------------------------------------------------------------------- ## Overview Most digital assets aren’t created in isolation — they often reuse elements from other assets. These source files are called **ingredients**. Ingredients preserve provenance across edits, keeping original Content Credentials and linking them into the new asset. *** ## Ingredient Objects Ingredients are recorded in the manifest’s `ingredients` array. Each entry contains metadata about the source file. Example: ```json "ingredients": [ { "title": "photo1.jpg", "format": "image/jpeg", "instance_id": "xmp:iid:315e20bf-10da-...", "thumbnail": { "format": "image/jpeg", "identifier": "photo1-thumb.jpg" }, "relationship": "componentOf", "active_manifest": "urn:uuid:8bb8ad50-ef2f-..." } ] ``` ### Key Properties * **title** (required): Usually filename * **format**: MIME type (e.g. `image/jpeg`) * **document\_id / instance\_id**: From XMP metadata * **thumbnail**: Thumbnail details * **active\_manifest**: Label of child manifest, if present * **relationship**: How the ingredient relates (see below) *** ## Ingredient Relationships | Value | Meaning | | ------------- | ---------------------------------------------------------------------- | | `parentOf` | The current asset is a derived version of this ingredient | | `componentOf` | This ingredient is combined to form the new asset | | `inputTo` | This ingredient was input to a process (e.g. AI model) that created it | *** ## Validation for Ingredients Ingredients may carry **their own manifests**. Que validates them when they are added. Ingredient validation does **not** imply the composed asset is valid. Each must be checked independently. *** ## Example: Invalid Ingredient ```json { "title": "E-sig-CA.jpg", "format": "image/jpeg", "relationship": "componentOf", "validation_status": [ { "code": "timeStamp.mismatch", "explanation": "timestamp message imprint did not match" }, { "code": "claimSignature.mismatch", "explanation": "claim signature is not valid" } ] } ``` *** ## Best Practices * Keep ingredient manifests included whenever possible to retain provenance. * Use relationships (`componentOf`, `inputTo`) consistently. * Don’t ignore ingredient-level validation status. # Validating Manifests URL: /docs/core/manifests/validating *** title: Validating Manifests description: How validation works for manifests and ingredients during processing. ---------------------------------------------------------------------------------- import { Tab, Tabs } from '@/components/tabs'; ## Overview Whenever Que processes an asset, it **validates the manifests** in the asset’s manifest store. Validation is critical for proving integrity, catching tampering, and checking credential trust. Validation runs both on the asset’s **active manifest** and on any **ingredients** included in it. *** ## Quickstart: Inspect Validation Results When you call [verify](/docs/verification), Que automatically validates the manifest. Validation issues appear inside the `validation_status` array. ```ts const result = await client.verifyAsset({ asset: { bucket: "que-assets-dev", key: "uploads/photo.jpg" }, mode: "detailed" }); console.log(result.report.validationStatus); ``` ```python res = client.verify_asset({ "asset": {"bucket": "que-assets-dev", "key": "uploads/photo.jpg"}, "mode": "detailed" }) print(res["report"]["validationStatus"]) ``` If `validation_status` is empty, the manifest is valid. Success is represented by absence of errors, not by a “true” flag. *** ## Common Validation Failures Validation failures can occur when: * The asset’s bytes change after signing. * Claims or assertions are missing or tampered. * Manifests are signed with an invalid credential. ### Example Error Codes | Code | Description | | ------------------------------ | ------------------------------------------------ | | `assertion.hashedURI.mismatch` | A stored hash and actual hash differ | | `assertion.dataHash.mismatch` | A declared data hash does not match | | `signingCredential.untrusted` | The signing certificate is not in the trust list | | `signingCredential.revoked` | The signing certificate has been revoked | | `signingCredential.expired` | Signing certificate has expired | *** ## Validating Ingredients Ingredients are validated on import. Their validation status is written to the ingredient’s own `validation_status` object. Don’t assume the top-level manifest covers ingredient validation. Always check the ingredient’s own status separately. *** ## Best Practices * Always inspect `validation_status` arrays on **both manifest and ingredients**. * Refresh the [trust list](/docs/api/utility/getTrustList) regularly. * Treat missing or revoked credentials as untrusted by default.