Sign and verify AI-generated images with three independent layers of provenance — in a single API call.
The OpenCorpo ComplianceAPI helps you comply with EU AI Act Article 50 and the emerging global standard for AI content disclosure. Every image you sign receives three interlocking layers of provenance that can be independently verified at any time:
All signed images are returned as PNG regardless of the input format. PNG is lossless, which is required to preserve the invisible watermark across downloads and re-uploads.
All API requests require an API key passed in the X-API-Key request header.
Obtaining an API Key
Sign in to the OpenCorpo dashboard and navigate to API Keys. Create a new key and copy the full key value — it is only shown once.
Keep your API key secret. Do not expose it in client-side JavaScript or commit it to version control. Always call the API from a server-side environment.
Sending the Key
Include the key in every request as an HTTP header:
X-API-Key: sk_live_your_api_key_here
Key Prefixes
| Prefix | Type | Description |
|---|---|---|
| sk_test_ | string | Test key — sandbox mode, no billing |
| sk_live_ | string | Live key — production, usage is billed |
Sign your first image in under 60 seconds.
1. Get your API key
Create a free account at compliance-app.opencorpo.com and generate an API key from the dashboard.
2. Sign an image
curl -X POST "https://i2dygqhmdw.eu-west-3.awsapprunner.com/v1/sign" \
-H "X-API-Key: sk_live_your_key" \
-F "file=@image.png" \
-F "generator=Stable Diffusion XL" \
-F "organization=Acme Inc" \
--output signed.png
import requests
with open("image.png", "rb") as f:
resp = requests.post(
"https://i2dygqhmdw.eu-west-3.awsapprunner.com/v1/sign",
headers={"X-API-Key": "sk_live_your_key"},
files={"file": ("image.png", f, "image/png")},
data={
"generator": "Stable Diffusion XL",
"organization": "Acme Inc",
},
)
resp.raise_for_status()
data = resp.json()
print(f"Asset ID: {data['asset_id']}")
print(f"Layers: {data['layers_applied']}")
# Download the signed PNG
download = requests.get(
f"https://i2dygqhmdw.eu-west-3.awsapprunner.com{data['download_url']}",
headers={"X-API-Key": "sk_live_your_key"},
)
with open("signed.png", "wb") as out:
out.write(download.content)
import FormData from "form-data";
import fs from "fs";
import fetch from "node-fetch";
const form = new FormData();
form.append("file", fs.createReadStream("image.png"), "image.png");
form.append("generator", "Stable Diffusion XL");
form.append("organization", "Acme Inc");
const res = await fetch(
"https://i2dygqhmdw.eu-west-3.awsapprunner.com/v1/sign",
{ method: "POST", headers: { "X-API-Key": "sk_live_your_key" }, body: form }
);
const data = await res.json();
console.log("Asset ID:", data.asset_id);
console.log("Layers: ", data.layers_applied);
3. Verify the signed image
curl -X POST "https://i2dygqhmdw.eu-west-3.awsapprunner.com/v1/verify" \
-H "X-API-Key: sk_live_your_key" \
-F "file=@signed.png"
import requests
with open("signed.png", "rb") as f:
resp = requests.post(
"https://i2dygqhmdw.eu-west-3.awsapprunner.com/v1/verify",
headers={"X-API-Key": "sk_live_your_key"},
files={"file": ("signed.png", f, "image/png")},
)
data = resp.json()
print(f"Status: {data['compliance_status']}") # compliant
print(f"Watermark: {data['has_watermark']}") # True
print(f"C2PA valid: {data['has_c2pa']}") # True
print(f"Fingerprint: {data['fingerprint_match']}") # True
All API requests should be made to the following base URL:
https://i2dygqhmdw.eu-west-3.awsapprunner.com
All endpoints accept and return JSON unless otherwise noted. File uploads use multipart/form-data. Signed images are returned as binary image/png.
Apply all three compliance layers to an AI-generated image and receive a signed PNG with embedded provenance data.
Request
Send a multipart/form-data request with the image file and optional provenance metadata.
Form Fields
| Field | Type | Description |
|---|---|---|
| file required | file | The image to sign. Accepted formats: JPEG, PNG, WebP. Maximum size: 20 MB. |
| generator optional | string | Name of the AI model or tool that generated the image. e.g. Stable Diffusion XL, DALL·E 3, Midjourney v6. Embedded in the C2PA manifest. |
| generator_version optional | string | Version of the AI model. e.g. 1.0.0. Embedded in the C2PA manifest. |
| organization optional | string | Organization name to attribute in the C2PA CreativeWork assertion. e.g. Acme Inc. |
Response 200 OK
{
"asset_id": "a3f1c9b2e041",
"status": "signed",
"layers_applied": [
"c2pa_metadata",
"invisible_watermark",
"content_fingerprint"
],
"compliance": {
"eu_ai_act_article_50": true
},
"download_url": "/v1/download/a3f1c9b2e041",
"report_url": "/v1/report/a3f1c9b2e041",
"verify_url": "/v1/verify/a3f1c9b2e041",
"processing_ms": 843,
"file_size": 2471038
}
Response Fields
| Field | Type | Description |
|---|---|---|
| asset_id | string | Unique identifier for this signed asset. Use this to download or report on the image. |
| status | string | Always "signed" on success. |
| layers_applied | string[] | List of the compliance layers successfully applied to the image. |
| compliance.eu_ai_act_article_50 | boolean | Whether the image meets the EU AI Act Article 50 disclosure requirements. |
| download_url | string | Path to download the signed PNG. Prepend the base URL. |
| report_url | string | Path to retrieve the full compliance report as JSON. |
| processing_ms | integer | Total server-side processing time in milliseconds. |
| file_size | integer | Size of the signed output file in bytes. |
Full Example
curl -X POST "https://i2dygqhmdw.eu-west-3.awsapprunner.com/v1/sign" \
-H "X-API-Key: sk_live_your_api_key" \
-F "file=@portrait.jpg" \
-F "generator=Midjourney v6" \
-F "generator_version=6.0" \
-F "organization=Acme Media" \
-o response.json
import requests
API_KEY = "sk_live_your_api_key"
BASE = "https://i2dygqhmdw.eu-west-3.awsapprunner.com"
def sign_image(path: str, generator: str, org: str) -> dict:
with open(path, "rb") as f:
mime = "image/png" if path.endswith(".png") else "image/jpeg"
resp = requests.post(
f"{BASE}/v1/sign",
headers={"X-API-Key": API_KEY},
files={"file": (path, f, mime)},
data={"generator": generator, "organization": org},
timeout=60,
)
resp.raise_for_status()
return resp.json()
result = sign_image("portrait.jpg", "Midjourney v6", "Acme Media")
print(result)
import FormData from "form-data";
import fs from "fs";
import fetch from "node-fetch";
const API_KEY = "sk_live_your_api_key";
const BASE = "https://i2dygqhmdw.eu-west-3.awsapprunner.com";
async function signImage(filePath, generator, organization) {
const form = new FormData();
form.append("file", fs.createReadStream(filePath));
form.append("generator", generator);
form.append("organization", organization);
const res = await fetch(`${BASE}/v1/sign`, {
method: "POST",
headers: { "X-API-Key": API_KEY, ...form.getHeaders() },
body: form,
});
if (!res.ok) {
const err = await res.json();
throw new Error(err.detail ?? `HTTP ${res.status}`);
}
return res.json();
}
const result = await signImage("portrait.jpg", "Midjourney v6", "Acme Media");
console.log(result);
<?php
$apiKey = 'sk_live_your_api_key';
$base = 'https://i2dygqhmdw.eu-west-3.awsapprunner.com';
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "$base/v1/sign",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_HTTPHEADER => ["X-API-Key: $apiKey"],
CURLOPT_POSTFIELDS => [
'file' => new CURLFile('portrait.jpg', 'image/jpeg'),
'generator' => 'Midjourney v6',
'organization' => 'Acme Media',
],
]);
$body = curl_exec($curl);
$result = json_decode($body, true);
echo $result['asset_id'];
?>
Check whether an image carries valid OpenCorpo provenance data. Works on any image — returns detection results for each compliance layer independently.
Request
Send a multipart/form-data request with the image to verify.
| Field | Type | Description |
|---|---|---|
| file required | file | The image to verify. Accepted: JPEG, PNG, WebP. Maximum size: 20 MB. |
Response 200 OK
{
"compliance_status": "compliant",
"has_watermark": true,
"watermark_id": "OPENCORPO:a3f1c9b2e041",
"has_c2pa": true,
"c2pa_details": {
"claim_generator": "OpenCorpo ComplianceAPI/0.1.0",
"title": "portrait.jpg",
"assertions_count": 2
},
"fingerprint_match": true,
"matched_asset_id": "a3f1c9b2e041"
}
{
"compliance_status": "non-compliant",
"has_watermark": false,
"watermark_id": null,
"has_c2pa": false,
"c2pa_details": null,
"c2pa_error": "No C2PA manifest found in this file",
"fingerprint_match": false,
"matched_asset_id": null
}
Response Fields
| Field | Type | Description |
|---|---|---|
| compliance_status | string | "compliant" all 3 layers detected · "partial" 1–2 layers detected · "non-compliant" no layers detected |
| has_watermark | boolean | Whether an OpenCorpo invisible watermark was found in the pixel data. |
| watermark_id | string | null | The identifier embedded in the watermark, in the format OPENCORPO:{asset_id}. |
| has_c2pa | boolean | Whether a valid C2PA Content Credentials manifest was found. |
| c2pa_details | object | null | Parsed manifest fields: claim_generator, title, assertions_count. |
| c2pa_error | string | null | Technical detail when C2PA detection fails. Useful for diagnosing why a manifest is absent or invalid. |
| fingerprint_match | boolean | Whether the image's perceptual hash matches a record in the OpenCorpo registry. |
| matched_asset_id | string | null | The asset ID of the matching registry entry, if a fingerprint match was found. |
The fingerprint check compares the image against assets signed by your account only. An image signed by a different OpenCorpo user will return fingerprint_match: false even if it is otherwise fully compliant.
Full Example
curl -X POST "https://i2dygqhmdw.eu-west-3.awsapprunner.com/v1/verify" \
-H "X-API-Key: sk_live_your_api_key" \
-F "file=@signed.png"
import requests
def verify_image(path: str) -> dict:
with open(path, "rb") as f:
resp = requests.post(
"https://i2dygqhmdw.eu-west-3.awsapprunner.com/v1/verify",
headers={"X-API-Key": "sk_live_your_api_key"},
files={"file": (path, f, "image/png")},
timeout=30,
)
resp.raise_for_status()
return resp.json()
result = verify_image("signed.png")
if result["compliance_status"] == "compliant":
print("✓ Fully compliant — all 3 layers verified")
print(f" Signed by: {result['c2pa_details']['claim_generator']}")
print(f" Asset ID: {result['matched_asset_id']}")
elif result["compliance_status"] == "partial":
print("⚠ Partial compliance")
else:
print("✗ Not compliant — no provenance data found")
import FormData from "form-data";
import fs from "fs";
import fetch from "node-fetch";
const form = new FormData();
form.append("file", fs.createReadStream("signed.png"));
const res = await fetch(
"https://i2dygqhmdw.eu-west-3.awsapprunner.com/v1/verify",
{
method: "POST",
headers: { "X-API-Key": "sk_live_your_api_key", ...form.getHeaders() },
body: form,
}
);
const data = await res.json();
console.log("Status:", data.compliance_status);
console.log("Watermark:", data.has_watermark);
console.log("C2PA:", data.has_c2pa);
console.log("Fingerprint:", data.fingerprint_match);
Retrieve the signed PNG file for a previously signed asset.
Path Parameters
| Parameter | Type | Description |
|---|---|---|
| asset_id required | string | The asset ID returned by /v1/sign. |
Response
Returns the signed PNG file as a binary stream with Content-Type: image/png and a Content-Disposition: attachment header.
curl "https://i2dygqhmdw.eu-west-3.awsapprunner.com/v1/download/a3f1c9b2e041" \
-H "X-API-Key: sk_live_your_api_key" \
--output signed.png
import requests
resp = requests.get(
"https://i2dygqhmdw.eu-west-3.awsapprunner.com/v1/download/a3f1c9b2e041",
headers={"X-API-Key": "sk_live_your_api_key"},
stream=True,
)
resp.raise_for_status()
with open("signed.png", "wb") as f:
for chunk in resp.iter_content(chunk_size=8192):
f.write(chunk)
Retrieve a full compliance report for a signed asset, including a checklist against EU AI Act Article 50 requirements.
Path Parameters
| Parameter | Type | Description |
|---|---|---|
| asset_id required | string | The asset ID returned by /v1/sign. |
Response 200 OK
{
"report_id": "rpt_a3f1c9b2e041",
"asset_id": "a3f1c9b2e041",
"generated_at": "2025-11-14T09:22:04Z",
"asset": {
"asset_id": "a3f1c9b2e041",
"filename": "portrait.jpg",
"original_content_type": "image/jpeg",
"content_type": "image/png",
"generator": "Midjourney v6",
"organization": "Acme Media",
"signed_at": "2025-11-14T09:21:58Z",
"file_size": 2471038,
"processing_ms": 843
},
"compliance_checklist": {
"article_50_2_machine_readable_marking": {
"status": "pass",
"method": "C2PA v2 manifest with signed provenance chain"
},
"article_50_2_watermarking": {
"status": "pass",
"method": "Invisible spatial-domain watermark embedded in image"
},
"article_50_2_content_fingerprint": {
"status": "pass",
"method": "Perceptual hash stored in content registry"
},
"code_of_practice_multi_layer": {
"status": "pass",
"note": "3 of 3 layers implemented per EU Code of Practice draft"
},
"digital_source_type": {
"status": "pass",
"value": "trainedAlgorithmicMedia (IPTC vocabulary)"
}
}
}
A simple health check endpoint for uptime monitoring and load balancer probes. Does not require authentication.
{
"status": "ok",
"timestamp": "2025-11-14T09:22:04.123456Z"
}
OpenCorpo applies three independent, complementary layers of provenance to every signed image. Each layer can survive scenarios the others cannot.
| Layer | Survives metadata strip? | Survives re-save as PNG? | Survives screenshot? |
|---|---|---|---|
| Invisible Watermark | ✓ | ✓ | ✗ |
| C2PA Metadata | ✗ | ✓ | ✗ |
| Perceptual Fingerprint | ✓ | ✓ | ✓ |
🔏 Invisible Watermark
The watermark is embedded using LSB (Least Significant Bit) steganography on the blue channel of the image. The technique modifies the lowest-order bit of selected pixel values to encode a sequence of bits representing the message OPENCORPO:{asset_id}.
How it works
- The image is converted to RGB and the blue channel is extracted as a flat array.
- A deterministic set of pixel positions is selected using a SHA-256 seed derived from the server's watermark key.
- The ASCII message is encoded as bits and written one bit per pixel into the LSB of each selected blue pixel.
- A null terminator byte marks the end of the message.
Why PNG output?
LSB watermarks are destroyed by JPEG compression, which modifies pixel values as part of DCT encoding. All signed images are therefore output as lossless PNG to preserve the watermark across subsequent downloads and re-uploads.
The watermark will not survive screenshot capture, re-encoding to JPEG, or significant image manipulation (resizing, cropping, colour grading). It is designed to survive lossless round-trips and normal PNG re-saves.
📜 C2PA Metadata
The C2PA (Coalition for Content Provenance and Authenticity) standard defines a cryptographically signed manifest format for embedding provenance data directly into a media file. OpenCorpo uses C2PA v2 with ECDSA-SHA256 signing.
What is embedded
- c2pa.actions — Records the
c2pa.createdaction with the AI generator name, version, and IPTCtrainedAlgorithmicMediadigital source type. - stds.schema-org.CreativeWork — Records the author organization using Schema.org vocabulary.
Certificate chain
OpenCorpo generates a test CA and end-entity certificate pair using P-256 (secp256r1) elliptic curve keys. In production environments, certificates signed by a trusted CA registered with the C2PA Trust List should be used for full cross-platform verification.
Viewing Content Credentials
C2PA-signed images can be inspected using the Content Credentials viewer at contentcredentials.org/verify, or any C2PA-compatible tool.
🔍 Content Fingerprint
A perceptual hash (average hash, 64-bit) is computed from the watermarked image and stored in the OpenCorpo registry at signing time.
Algorithm
- The image is converted to grayscale and resized to 8×8 pixels.
- The average pixel value is computed.
- Each pixel is compared to the average —
1if above,0if below — producing a 64-bit binary string.
During verification, the same hash is computed from the uploaded image and compared against every asset in the account's registry. If a match is found, the matched_asset_id is returned.
The perceptual fingerprint is robust to minor colour shifts and lossless transformations. It will even match a screenshot of the signed image in many cases, making it the most persistent of the three layers.
The API returns standard HTTP status codes. Error responses include a detail field with a human-readable message.
{
"detail": "Unsupported type: image/gif. Allowed: {'image/jpeg', 'image/png', 'image/webp'}"
}
| Status | Code | Cause & Resolution |
|---|---|---|
| 400 | Bad Request | Invalid file format, file too large (>20 MB), or missing required fields. Check the detail field for the specific reason. |
| 401 | Unauthorized | The X-API-Key header is missing. Add your API key to the request. |
| 403 | Forbidden | The provided API key is invalid or has been revoked. Check the key in your dashboard. |
| 404 | Not Found | The asset_id does not exist in the registry, or the signed file was not found on the server. |
| 422 | Unprocessable Entity | The request body could not be parsed. Ensure you are sending multipart/form-data with the correct field names. |
| 500 | Internal Server Error | An unexpected server error occurred. This is likely transient — retry after a few seconds. If the issue persists, contact support. |
Usage is metered per account. Limits apply to the total number of /v1/sign and /v1/verify calls per calendar month.
| Plan | Monthly Sign Calls | Monthly Verify Calls | API Keys | Support |
|---|---|---|---|---|
| Trial | 100 | 100 | 1 | Community |
| Pro | 5,000 | 10,000 | 5 | |
| Business | Unlimited | Unlimited | Unlimited | Priority |
Rate limit headers are included in every response:
| Header | Description |
|---|---|
| X-RateLimit-Limit | Your monthly call limit for this endpoint type. |
| X-RateLimit-Remaining | Calls remaining this calendar month. |
| X-RateLimit-Reset | Unix timestamp of when the limit resets (first of next month, UTC). |
When you exceed your monthly limit, the API returns 429 Too Many Requests. Upgrade your plan to increase limits.
| Format | MIME Type | Upload | Signed Output |
|---|---|---|---|
| JPEG | image/jpeg | ✓ | Converted to PNG |
| PNG | image/png | ✓ | PNG |
| WebP | image/webp | ✓ | Converted to PNG |
| GIF | image/gif | ✗ | — |
| TIFF | image/tiff | ✗ | — |
All signed output is PNG regardless of input format. This is required to preserve the invisible watermark, which is destroyed by lossy compression such as JPEG encoding.
A complete Python script that signs an image, verifies it, and downloads the signed file.
"""
OpenCorpo ComplianceAPI — Python example
pip install requests
"""
import requests
import sys
API_KEY = "sk_live_your_api_key"
BASE = "https://i2dygqhmdw.eu-west-3.awsapprunner.com"
HEADERS = {"X-API-Key": API_KEY}
def sign(image_path: str) -> dict:
"""Sign an image and return the API response."""
print(f"Signing {image_path}...")
with open(image_path, "rb") as f:
mime = "image/png" if image_path.endswith(".png") else "image/jpeg"
resp = requests.post(
f"{BASE}/v1/sign",
headers=HEADERS,
files={"file": (image_path, f, mime)},
data={"generator": "My AI App", "organization": "Acme Inc"},
timeout=60,
)
resp.raise_for_status()
return resp.json()
def download(asset_id: str, output_path: str):
"""Download the signed PNG for an asset."""
print(f"Downloading signed image → {output_path}")
resp = requests.get(f"{BASE}/v1/download/{asset_id}", headers=HEADERS, stream=True)
resp.raise_for_status()
with open(output_path, "wb") as f:
for chunk in resp.iter_content(chunk_size=8192):
f.write(chunk)
def verify(image_path: str) -> dict:
"""Verify an image and return the compliance result."""
print(f"Verifying {image_path}...")
with open(image_path, "rb") as f:
resp = requests.post(
f"{BASE}/v1/verify",
headers=HEADERS,
files={"file": (image_path, f, "image/png")},
timeout=30,
)
resp.raise_for_status()
return resp.json()
if __name__ == "__main__":
image = sys.argv[1] if len(sys.argv) > 1 else "image.jpg"
# 1. Sign
result = sign(image)
asset_id = result["asset_id"]
print(f" Asset ID : {asset_id}")
print(f" Layers : {', '.join(result['layers_applied'])}")
# 2. Download
signed_path = f"signed_{asset_id}.png"
download(asset_id, signed_path)
# 3. Verify the signed file
v = verify(signed_path)
status = v["compliance_status"]
layers_ok = sum([v["has_watermark"], v["has_c2pa"], v["fingerprint_match"]])
print(f"\nVerification result: {status.upper()} ({layers_ok}/3 layers)")
print(f" Watermark : {'✓' if v['has_watermark'] else '✗'} {v.get('watermark_id','')}")
print(f" C2PA : {'✓' if v['has_c2pa'] else '✗'}")
print(f" Fingerprint : {'✓' if v['fingerprint_match'] else '✗'}")
A complete Node.js script using the native fetch API (Node 18+).
/**
* OpenCorpo ComplianceAPI — Node.js example
* Node 18+ (native fetch) or: npm install node-fetch form-data
*/
import { createReadStream, createWriteStream } from "fs";
import { pipeline } from "stream/promises";
const API_KEY = "sk_live_your_api_key";
const BASE = "https://i2dygqhmdw.eu-west-3.awsapprunner.com";
async function sign(imagePath) {
const { default: FormData } = await import("form-data");
const form = new FormData();
form.append("file", createReadStream(imagePath));
form.append("generator", "My AI App");
form.append("organization", "Acme Inc");
const res = await fetch(`${BASE}/v1/sign`, {
method: "POST",
headers: { "X-API-Key": API_KEY, ...form.getHeaders() },
body: form,
});
if (!res.ok) throw new Error(`Sign failed: ${res.status}`);
return res.json();
}
async function download(assetId, outputPath) {
const res = await fetch(`${BASE}/v1/download/${assetId}`, {
headers: { "X-API-Key": API_KEY },
});
if (!res.ok) throw new Error(`Download failed: ${res.status}`);
await pipeline(res.body, createWriteStream(outputPath));
}
async function verify(imagePath) {
const { default: FormData } = await import("form-data");
const form = new FormData();
form.append("file", createReadStream(imagePath));
const res = await fetch(`${BASE}/v1/verify`, {
method: "POST",
headers: { "X-API-Key": API_KEY, ...form.getHeaders() },
body: form,
});
if (!res.ok) throw new Error(`Verify failed: ${res.status}`);
return res.json();
}
// --- Run ---
const result = await sign("image.jpg");
console.log("Asset ID:", result.asset_id);
console.log("Layers: ", result.layers_applied.join(", "));
const signedPath = `signed_${result.asset_id}.png`;
await download(result.asset_id, signedPath);
console.log("Downloaded →", signedPath);
const v = await verify(signedPath);
console.log("\nCompliance:", v.compliance_status.toUpperCase());
console.log(" Watermark :", v.has_watermark ? "✓" : "✗");
console.log(" C2PA :", v.has_c2pa ? "✓" : "✗");
console.log(" Fingerprint:", v.fingerprint_match ? "✓" : "✗");
All API operations as copy-paste cURL commands.
# ── Sign an image ──────────────────────────────────────────────────────────
curl -X POST "https://i2dygqhmdw.eu-west-3.awsapprunner.com/v1/sign" \
-H "X-API-Key: sk_live_your_key" \
-F "file=@image.jpg" \
-F "generator=Stable Diffusion XL" \
-F "organization=Acme Inc"
# ── Verify an image ─────────────────────────────────────────────────────────
curl -X POST "https://i2dygqhmdw.eu-west-3.awsapprunner.com/v1/verify" \
-H "X-API-Key: sk_live_your_key" \
-F "file=@signed.png"
# ── Download a signed image ─────────────────────────────────────────────────
curl "https://i2dygqhmdw.eu-west-3.awsapprunner.com/v1/download/a3f1c9b2e041" \
-H "X-API-Key: sk_live_your_key" \
--output signed.png
# ── Get compliance report ───────────────────────────────────────────────────
curl "https://i2dygqhmdw.eu-west-3.awsapprunner.com/v1/report/a3f1c9b2e041" \
-H "X-API-Key: sk_live_your_key"
# ── Health check (no auth required) ────────────────────────────────────────
curl "https://i2dygqhmdw.eu-west-3.awsapprunner.com/health"