API cURL Examples¶
Examples in this page target the current v2 HTTP surface implemented by service-http.
For the canonical route contract, see API Reference.
Health Check¶
Expected status: 200 OK
C2PA Video Signing¶
Async¶
curl -X POST http://localhost:8080/v2/c2pa/video \
-H "Content-Type: application/json" \
-d '{
"inputs": [
{
"type": "remote_file_url",
"url": "https://example.com/video.mp4",
"filename_hint": "video.mp4"
}
],
"params": {},
"output": {
"type": "local",
"name": "signed-videos"
}
}'
Sync¶
curl -X POST http://localhost:8080/v2/sync/c2pa/video \
-H "Content-Type: application/json" \
-d '{
"inputs": [
{
"type": "local_file_path",
"path": "./tests/fixtures/video/mp4/video1.mp4"
}
],
"params": {}
}'
With Custom Assertions And Parent File¶
Custom assertions embed arbitrary JSON in the signed manifest; a parent file is read for its embedded C2PA manifest and attached as a provenance ingredient.
curl -X POST http://localhost:8080/v2/sync/c2pa/video \
-H "Content-Type: application/json" \
-d '{
"inputs": [
{ "type": "local_file_path", "path": "./tests/fixtures/video/mp4/video1.mp4" }
],
"params": {
"assertions": [
{ "label": "com.example.rights", "data": { "owner": "ACME Media", "license": "CC-BY-4.0" } }
],
"parent": {
"type": "file",
"source": { "type": "local_file_path", "path": "./tests/fixtures/video/mp4/parent.mp4" }
}
}
}'
Custom assertions and parent provenance work on all four C2PA endpoints (/v2/c2pa/video, /v2/c2pa/audio, /v2/c2pa/fragmented, /v2/c2pa/package) under params.assertions, params.parent, and params.parent_overrides, and can be combined in one request. The fragmented and package examples below show the same fields. See Custom Assertions and Parent Provenance.
C2PA Audio Signing¶
Single audio files only: mp3, wav, flac, m4a, and mp4/m4v for audio-only assets. The request shape matches video signing.
Async¶
curl -X POST http://localhost:8080/v2/c2pa/audio \
-H "Content-Type: application/json" \
-d '{
"inputs": [
{
"type": "remote_file_url",
"url": "https://example.com/interview.wav",
"filename_hint": "interview.wav"
}
],
"params": {},
"output": {
"type": "local",
"name": "signed-audio"
}
}'
Sync¶
curl -X POST http://localhost:8080/v2/sync/c2pa/audio \
-H "Content-Type: application/json" \
-d '{
"inputs": [
{
"type": "local_file_path",
"path": "./tests/fixtures/audio/mp3/tone.mp3"
}
],
"params": {}
}'
With Custom Assertions And Parent File¶
params.assertions, params.parent, and params.parent_overrides behave exactly as in the video examples above.
curl -X POST http://localhost:8080/v2/sync/c2pa/audio \
-H "Content-Type: application/json" \
-d '{
"inputs": [
{ "type": "local_file_path", "path": "./tests/fixtures/audio/mp3/tone.mp3" }
],
"params": {
"assertions": [
{ "label": "com.example.rights", "data": { "owner": "ACME Media", "license": "CC-BY-4.0" } }
],
"parent": { "type": "reference", "reference": "urn:example:parent-asset" }
}
}'
C2PA Fragmented Signing¶
Async¶
curl -X POST http://localhost:8080/v2/c2pa/fragmented \
-H "Content-Type: application/json" \
-d '{
"inputs": [
{
"type": "remote_folder_url",
"url": "s3://media-bucket/publication/"
}
],
"params": {
"playlist_pattern": "**/*.m3u8",
"init_pattern": "**/init*.mp4",
"frag_pattern": "seg*.m4s"
}
}'
Sync¶
curl -X POST http://localhost:8080/v2/sync/c2pa/fragmented \
-H "Content-Type: application/json" \
-d '{
"inputs": [
{
"type": "local_folder_path",
"path": "./tests/fixtures/hls/publication"
}
],
"params": {
"playlist_pattern": "**/*.m3u8",
"init_pattern": "**/init*.mp4",
"frag_pattern": "seg*.m4s"
}
}'
With Custom Assertions And Parent¶
The same assertions, parent, and parent_overrides fields used for video apply here. They are embedded into every signed init segment of the publication.
curl -X POST http://localhost:8080/v2/sync/c2pa/fragmented \
-H "Content-Type: application/json" \
-d '{
"inputs": [
{ "type": "local_folder_path", "path": "./tests/fixtures/hls/publication" }
],
"params": {
"playlist_pattern": "**/*.m3u8",
"init_pattern": "**/init*.mp4",
"frag_pattern": "seg*.m4s",
"assertions": [
{ "label": "com.example.rights", "data": { "owner": "ACME Media", "license": "CC-BY-4.0" } }
],
"parent": {
"type": "file",
"source": { "type": "local_file_path", "path": "./tests/fixtures/video/mp4/parent.mp4" }
}
}
}'
Media Packaging¶
Async¶
curl -X POST http://localhost:8080/v2/package \
-H "Content-Type: application/json" \
-d '{
"inputs": [
{
"type": "local_folder_path",
"path": "./tests/fixtures/video/mp4"
}
],
"params": {}
}'
Sync¶
curl -X POST http://localhost:8080/v2/sync/package \
-H "Content-Type: application/json" \
-d '{
"inputs": [
{
"type": "local_folder_path",
"path": "./tests/fixtures/video/mp4"
}
],
"params": {}
}'
Package And Sign¶
Async¶
curl -X POST http://localhost:8080/v2/c2pa/package \
-H "Content-Type: application/json" \
-d '{
"inputs": [
{
"type": "local_folder_path",
"path": "./tests/fixtures/video/mp4"
}
],
"params": {
"playlist_pattern": "**/*.m3u8",
"init_pattern": "**/init*.mp4",
"frag_pattern": "seg*.m4s"
}
}'
Sync¶
curl -X POST http://localhost:8080/v2/sync/c2pa/package \
-H "Content-Type: application/json" \
-d '{
"inputs": [
{
"type": "local_folder_path",
"path": "./tests/fixtures/video/mp4"
}
],
"params": {}
}'
With Custom Assertions And Parent¶
Packaging and signing accepts the same assertions, parent, and parent_overrides fields. Here a reference-style parent is attached as a bare provenance ingredient.
curl -X POST http://localhost:8080/v2/sync/c2pa/package \
-H "Content-Type: application/json" \
-d '{
"inputs": [
{ "type": "local_folder_path", "path": "./tests/fixtures/video/mp4" }
],
"params": {
"assertions": [
{ "label": "com.example.rights", "data": { "owner": "ACME Media" } }
],
"parent": { "type": "reference", "reference": "urn:example:parent-asset" }
}
}'
Request-Selected S3 Output¶
Available only when the service is built with --features s3 and the target bucket is configured in output_sinks.s3.buckets. Naming lives in the output section; the object key is base_prefix / prefix / name / <per-input-leaf> (here .../exports/c2pa/spring-campaign/video_c2pa.mp4).
curl -X POST http://localhost:8080/v2/c2pa/video \
-H "Content-Type: application/json" \
-d '{
"inputs": [
{
"type": "remote_file_url",
"url": "s3://media-bucket/input/video.mp4",
"filename_hint": "video.mp4"
}
],
"params": {},
"output": {
"type": "s3",
"bucket": "processed-media",
"prefix": "exports/c2pa",
"name": "spring-campaign"
}
}'
Request-Selected Local Output¶
The local variant writes to the service host's filesystem under output_sinks.local.base_dir (useful for local or self-hosted runs). Whether single-file outputs keep the input filename or get the _c2pa marker is set per sink in the service config (output_sinks.local.naming), not in the request.
curl -X POST http://localhost:8080/v2/sync/c2pa/video \
-H "Content-Type: application/json" \
-d '{
"inputs": [
{ "type": "local_file_path", "path": "./tests/fixtures/video/mp4/video1.mp4" }
],
"params": {},
"output": {
"type": "local",
"name": "spring-campaign"
}
}'
Job Status Query¶
After an async submission:
Placeholder Endpoints¶
The watermark endpoints are visible but not implemented:
Expected status: 501 Not Implemented
Example Responses¶
Async Submission¶
Completed Sync Response¶
{
"job_id": "01234567-89ab-cdef-0123-456789abcdef",
"status": "succeeded",
"results": [
{
"input_index": 0,
"status": "succeeded",
"output_path": "./.artifacts/output/signed-videos/video1_c2pa.mp4"
}
],
"timing": {
"queued_at": "2026-04-09T08:00:00Z",
"started_at": "2026-04-09T08:00:01Z",
"finished_at": "2026-04-09T08:00:05Z"
}
}
Polling Response¶
{
"job_id": "01234567-89ab-cdef-0123-456789abcdef",
"status": "running",
"results": [],
"timing": {
"queued_at": "2026-04-09T08:00:00Z",
"started_at": "2026-04-09T08:00:01Z"
}
}
Notes¶
- Async responses do not include a
status_url; pollGET /v2/jobs/{id}. - Sync endpoints return
408if the job does not finish beforeserver.sync_timeout_ms. - Omitting
outputis equivalent to{"type": "local"}with no fields. - The
outputsection carries the destination and placement (type,bucket,prefix,name), notparams. The file-leaf naming mode is set per output sink in the service config (output_sinks.*.naming), not per request. Each input's leaf is derived from the input: files become{stem}_c2pa.{ext}(or the plain filename when the sink is configuredin_place); folders/publications use the input folder name. See Output Selection.