Skip to content

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

curl -i http://localhost:8080/v2/health

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:

curl http://localhost:8080/v2/jobs/01234567-89ab-cdef-0123-456789abcdef

Placeholder Endpoints

The watermark endpoints are visible but not implemented:

curl -i -X POST http://localhost:8080/v2/watermark
curl -i http://localhost:8080/v2/watermark/wm-1

Expected status: 501 Not Implemented

Example Responses

Async Submission

{
  "job_id": "01234567-89ab-cdef-0123-456789abcdef",
  "status": "queued"
}

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; poll GET /v2/jobs/{id}.
  • Sync endpoints return 408 if the job does not finish before server.sync_timeout_ms.
  • Omitting output is equivalent to {"type": "local"} with no fields.
  • The output section carries the destination and placement (type, bucket, prefix, name), not params. 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 configured in_place); folders/publications use the input folder name. See Output Selection.

See Also