Python SDK
The originalpictures-sdk distribution (import name originalpictures) is a typed client for
the public API. It is generated from the same contract as the API reference. It covers
all 109 operations and can call the 107 consumer operations. The two provider-ingress callbacks, for
the Clerk and Stripe webhooks, are typed but deliberately not callable.
- Python 3.12
- Dependency:
httpx - Sync (
Client) and async (AsyncClient) clients
Configure a client
Section titled “Configure a client”import osfrom originalpictures import Client
client = Client( base_url=os.environ.get("OP_API_BASE_URL", "https://dev-api.originalpictures.com"), api_key=os.environ["OP_API_KEY"],)base_url can include a path prefix. A base URL with a query string or fragment is rejected
before any request is sent. The client does not read environment variables by itself: pass the key
explicitly, and never put it in command arguments or logs.
Calling operations
Section titled “Calling operations”Every contract operation is a method named after its operationId in snake case. Examples are
create_upload, complete_upload, accept_signing_job, get_job, create_verification,
get_verification, and list_recipes. Methods take keyword arguments body=, parameters=
(path and query), and headers=, and return typed dictionaries.
Sign a file
Section titled “Sign a file”import hashlibfrom pathlib import Pathfrom originalpictures import idempotency_key, poll, sign_job_terminal
path = Path("photo.jpg")sha256 = hashlib.sha256(path.read_bytes()).hexdigest()
upload = client.create_upload( body={ "byte_length": path.stat().st_size, "sha256": sha256, "declared_media_type": "image/jpeg", "mode": "single", "filename": path.name, })# Sends exactly the issued headers, never your API key, and checks length and SHA-256.client.upload_file(upload["upload"]["url"], path, sha256=sha256, headers=upload["upload"]["headers"])
completed = client.complete_upload( body={}, parameters={"upload_id": upload["upload_id"]}, headers={"Idempotency-Key": idempotency_key()},)
job = client.accept_signing_job( body={"asset_id": upload["asset_id"], "recipe_id": "rcp_system_sign_only"}, headers={"Idempotency-Key": idempotency_key()},)job = poll( lambda: client.get_job(parameters={"job_id": job["job_id"]}), sign_job_terminal, timeout=300,)print(job["state"], job["result"])Choose a mode and a watermark strength
Section titled “Choose a mode and a watermark strength”The three signing modes are rcp_system_sign (C2PA only), rcp_system_watermark_then_sign_unfingerprinted
(watermark, no fingerprint) and rcp_system_watermark_then_sign (watermark and fingerprints); see
Choose a mode. watermark_strength is subtle, balanced (the default)
or robust:
job = client.accept_signing_job( body={ "asset_id": upload["asset_id"], "recipe_id": "rcp_system_watermark_then_sign_unfingerprinted", "watermark_strength": "subtle", }, headers={"Idempotency-Key": idempotency_key()},)A key whose signing profile names a default recipe may omit
recipe_id; its other defaults fill the fields a request omits.
sign_job_terminal fails closed on inconsistent snapshots. For example, a completed job must
carry ready vfy_… evidence. For verifications, use create_verification with
verification_job_terminal. A vjob_… ID is only a pollable job, never evidence.
Polling
Section titled “Polling”poll enforces a wall-clock deadline, a bounded number of attempts, and backoff from interval up
to max_interval. It can be cancelled cooperatively through cancelled=. Synchronous polls run on
at most eight worker slots and fail with PollingCapacityError when every slot is busy.
poll_async cancels the in-flight coroutine at its deadline.
Transfers
Section titled “Transfers”upload_fileandupload_streamPUTbytes to an API-issued URL. They verify length and SHA-256 and never forward API authorization.download_fileanddownload_streamwrite to a staged temporary file. The file replaces the destination only after its length and SHA-256 match the values fromPOST /v1/assets/{asset_id}/download.
Webhooks
Section titled “Webhooks”verify_webhook(body, secret=…, event_id=…, timestamp=…, signature=…) checks a delivery against
the exact raw body. See Webhooks.
Errors
Section titled “Errors”API failures raise subclasses of ApiError: AuthenticationError, PermissionDeniedError,
NotFoundError, ConflictError, RequestValidationError, RateLimitError, and
ServiceUnavailableError. Each carries the response’s reason code, request_id, and
retryable flag. Transport problems raise TransportError or DeadlineExceededError, and
integrity failures raise IntegrityError. All of them derive from OriginalPicturesError. See
Errors.
