Giriş
API üç temel işi yapar:
- İşaretle (
POST /v1/marks) — GenAI uygulamanızın ürettiği bir görseli "AI ile üretildi" olarak kayda alır. EU AI Act Madde 50 içerik işaretleme yükümlülüğü için birincil kullanım budur. - Kaydet (
POST /v1/registrations) — tarayıcıda/istemcide hesaplanmış hash'leri kayda alır (ücretsiz web imzalama akışı bunu kullanır). - Doğrula (
GET /v1/verify) — bir görselin kayıtta olup olmadığını tam hash ile ya da algısal parmak iziyle (yeniden sıkıştırılmış, boyutlandırılmış kopyalar için) sorgular.
Her kayıt, toplu olarak düzenli aralıklarla zaman damgasıyla sabitlenir; kayıt kimse tarafından
geriye tarihlenemez ve kanıtı GET /v1/anchors/…/proof/… ile bağımsız olarak
denetlenebilir. v1 kapsamı yalnızca görseldir (JPG/PNG); video ve ses sonraki sürümlerde.
Kimlik doğrulama
Yazma endpoint'leri (marks, usage, webhook-endpoints,
sign-digest) Authorization header'ı ister:
Authorization: Bearer ozdna_live_…
Anahtarlar iki önekle gelir: ozdna_live_ (production) ve ozdna_test_
(test). Test anahtarıyla oluşan kayıtlar is_test olarak işaretlenir, zaman damgasına
ve kotaya dahil edilmez, herkese açık doğrulamada görünmez. Anahtar yalnızca oluşturulduğunda bir kez
gösterilir; sunucu ham anahtarı değil yalnızca SHA-256 özetini saklar. Doğrulama ve kayıt okuma
endpoint'leri anahtarsız çalışır (IP bazlı rate limit uygulanır).
Base URL
https://api.ozdna.com # tek host; test/production ayrımı anahtar önekiyle yapılır
Ayrı bir sandbox host'u yoktur — ozdna_test_ anahtarı aynı API'yi izole test kayıtlarıyla kullanır.
Parmak izleri
API görselin kendisini saklamaz; üç parmak izi ile çalışır:
| Alan | Biçim | Ne işe yarar |
|---|---|---|
sha256 | 64 hex | Dosyanın birebir kopyası — tek bayt değişirse eşleşmez. |
phash | 16 hex (64 bit) | Algısal hash — yeniden sıkıştırma, boyutlandırma, metadata silinmesine dayanır. |
pdq256 | 64 hex (256 bit) | İsteğe bağlı ikinci algısal hash; olası eşleşmeleri teyit eder, yanlış pozitifleri düşürür. |
Önerilen akış: parmak izlerini istemcide (tarayıcı veya sunucunuz) hesaplayın, yalnızca hash'leri gönderin. Görsel ağdan geçmez.
POST /v1/marks çalışıyor · yalnızca kayıt
AI ile üretilmiş bir görseli kayda alır. Kayıt türü her zaman ai_generated'dır — bu endpoint
üzerinden bir görseli "kamera çekimi" olarak işaretlemenin yolu yoktur.
Şu anki mod: yalnızca kayıt. Kayıt, parmak izi ve zaman damgası oluşur; görselin içine
C2PA manifest'i henüz gömülmez. Yanıttaki mode: "registry_only" ve
embed: false alanları bunu açıkça söyler. Sunucu tarafı gömme + imzalama geldiğinde
aynı endpoint işaretlenmiş görsel baytlarını döndürecek.
JSON gövde (önerilen)
| Alan | Tip | Açıklama |
|---|---|---|
sha256 zorunlu | string | 64 hex |
phash zorunlu | string | 16 hex |
file_mime zorunlu | string | image/jpeg · image/png |
pdq256 | string | 64 hex, isteğe bağlı |
file_bytes | integer | Dosya boyutu (bayt) |
title | string | ≤ 120 karakter, herkese açık kayıtta görünür |
generator | string | ≤ 100 karakter, örn. acme-imagegen/2.3. Gömme geldiğinde manifest'e yazılacak; şimdilik yok sayılır. |
is_test | boolean | Test kaydı (test anahtarında otomatik true) |
Örnek istek
curl -X POST https://api.ozdna.com/v1/marks \
-H "Authorization: Bearer ozdna_live_…" \
-H "Content-Type: application/json" \
-d '{
"sha256": "9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08",
"phash": "c4a2b1d8e0f39a57",
"file_mime": "image/png",
"title": "ürün görseli v1",
"generator": "acme-imagegen/2.3"
}'
Alternatif: multipart dosya yükleme
multipart/form-data ile file (JPG/PNG, ≤ 20 MB) gönderirseniz sha256'yı
sunucu hesaplar. Sunucu tarafı görsel çözümleme henüz olmadığından phash form alanı bu modda da
zorunludur; pdq256, title, is_test isteğe bağlıdır.
curl -X POST https://api.ozdna.com/v1/marks \
-H "Authorization: Bearer ozdna_live_…" \
-F "file=@output.png;type=image/png" \
-F "phash=c4a2b1d8e0f39a57"
Örnek yanıt 201 Created · aynı sha256 daha önce kayıtlıysa 200
{
"mode": "registry_only",
"embed": false,
"note": "No C2PA bytes embedded yet — record + fingerprint only (Sept spike).",
"record": {
"id": "rec_…",
"kind": "ai_generated",
"status": "registered",
"sha256": "9f86d081…0f00a08",
"phash": "c4a2b1d8e0f39a57",
"pdq256": null,
"created_at": "2026-10-08T14:03:22.113Z",
"url": "https://ozdna.com/r/rec_…"
},
"deduplicated": false
}
Aynı sha256 zaten kayıtlıysa yeni kayıt açılmaz; mevcut kayıt "deduplicated": true ile döner.
POST /v1/registrations çalışıyor
İstemcide hesaplanmış hash'leri kayda alır. Ücretsiz tarayıcı imzalama akışı (görsel tarayıcıda imzalanır,
sunucuya yalnızca hash gider) bu endpoint'i kullanır. Gövde marks ile aynıdır, ek olarak:
| Alan | Tip | Açıklama |
|---|---|---|
kind zorunlu | string | ai_generated · claimed_capture · unspecified |
claimed_capture bir beyandır: kaydı yapanın "bunu ben çektim" iddiasını kayda geçirir,
görselin gerçekten kamerayla çekildiğini kanıtlamaz.
GET /v1/verify çalışıyor
Bir görselin kayıtta olup olmadığını sorgular. İki mod:
| Parametre | Açıklama |
|---|---|
hash | 64 hex SHA-256 — birebir dosya eşleşmesi |
phash | 16 hex — algısal eşleşme (değiştirilmiş kopyalar) |
pdq | 64 hex, isteğe bağlı — algısal eşleşmeyi teyit eder |
deep | 1 = daha geniş arama (Hamming mesafesi ≤ 10'a kadar eksiksiz). Varsayılan arama ≤ 7'ye kadar eksiksizdir. |
Örnek — birebir eşleşme
curl "https://api.ozdna.com/v1/verify?hash=9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08"
{
"verdict": "EXACT_ANCHORED",
"match_type": "exact",
"confidence": "exact",
"hash": "9f86d081…0f00a08",
"record": {
"id": "rec_…",
"sha256": "9f86d081…0f00a08",
"phash": "c4a2b1d8e0f39a57",
"pdq256": null,
"status": "anchored",
"createdAt": "2026-10-08T14:03:22.113Z",
"anchoredAt": "2026-10-08T18:00:41Z",
"creatorDisplay": "Acme Imagegen"
}
}
Örnek — algısal eşleşme
curl "https://api.ozdna.com/v1/verify?phash=c4a2b1d8e0f39a51&pdq=…"
{
"verdict": "VISUAL_MATCH_HIGH",
"match_type": "perceptual",
"confidence": "high",
"phash_distance": 2,
"pdq_distance": 14,
"pdq_confirmed": true,
"show_as_match": true,
"record": { "id": "rec_…", "status": "anchored", "…": "…" },
"near_misses": []
}
Eşleşme yoksa "verdict": "NO_RECORD" döner; yakın ama eşik dışı adaylar
near_misses listesinde (en fazla 5) yer alır — bunlar eşleşme değildir.
planlı POST /v1/verify — dosya yükleyerek doğrulama
(sunucu hash'leri hesaplar, gömülü manifest'i kontrol eder) ve ?url= modu MVP kapsamında.
GET /v1/records/{id} çalışıyor
Kaydın herkese açık alanlarını döndürür: id, kind, sha256,
phash, pdq256, status (registered → anchored),
created_at, anchored_at, title, file_mime,
anchor_batch_id, creator_display. Herkese açık kayıt sayfası: https://ozdna.com/r/{id}.
GET /v1/anchors/{batch_id}/proof/{record_id} kısmen
Kaydın zaman damgası kanıtı: hangi toplu işlemde (batch_id) ve hangi sırada
(leaf_index) yer aldığı, toplu işlemin kök özeti (merkle_root) ve kamuya açık
işlem referansı (tx_hash). Kanıt, ozDNA'ya hiç sormadan bağımsız olarak denetlenebilecek
şekilde tasarlanmıştır.
Şu an: proof dizisi (kardeş hash'ler) henüz boş döner; toplu işlem servisi
kanıtları kalıcı olarak yazmaya başladığında doldurulacak.
GET /v1/usage çalışıyor
{
"month": "2026-10",
"plan": "starter",
"events": { "mark": 412, "registration": 0, "verify_file": 0, "sign_digest": 0 },
"quotas": { "mark": 2000, "registration": 2000, "verify_file": 0, "sign_digest": 0 }
}
Doğrulama kararları
verdict alanı sabit bir kelime dağarcığından gelir. match_type ve
confidence, yalnızca kaba kategoriye ihtiyaç duyan istemciler için bu kararın özetidir.
| verdict | Anlamı |
|---|---|
EXACT_ANCHORED | Birebir dosya kayıtta, zaman damgası onaylandı |
EXACT_PENDING | Birebir dosya kayıtta, zaman damgası sırada |
VISUAL_MATCH_HIGH | Görsel olarak aynı (değiştirilmiş kopya), yüksek güven |
VISUAL_MATCH_PROBABLE | Muhtemel eşleşme, orta güven |
NEAR_MISS | Benzer ama eşik dışında — eşleşme sayılmaz |
NO_RECORD | Kayıtta bulunamadı |
SIGNED_BY_OZDNA · THIRD_PARTY_CREDENTIALS · SIGNATURE_BROKEN · SIGNATURE_REVOKED | planlı Gömülü manifest kontrolü (dosya yüklemeli doğrulama) ile gelecek |
Önemli: ozDNA kaydı belgeler, üretimi değil. Zaman damgası bir parmak izinin
kayda ne zaman girdiğini gösterir — görselin ne zaman veya kim tarafından yapıldığını ya da AI ile üretilip
üretilmediğini değil. NO_RECORD, "bu görsel sahte" anlamına gelmez.
Webhook'lar uç nokta yönetimi teslimat planlı
Uç nokta {"url": "https://…", "description": "…"} ile oluşturulur; URL https://
olmalıdır. Yanıttaki secret yalnızca bir kez gösterilir.
Olaylar (teslimat MVP kapsamında): record.anchored, mark.created,
usage.threshold_reached (kotanın %80 ve %100'ünde).
{
"id": "evt_…",
"type": "record.anchored",
"created_at": "2026-10-08T18:00:45Z",
"data": { "record_id": "rec_…", "batch_id": "bat_…" }
}
İmza doğrulama: Ozdna-Signature: t=<unix>,v1=<hex> header'ı,
HMAC-SHA256(secret, t + "." + ham_gövde) ile doğrulanır. t ile şimdiki zaman arasında
5 dakikadan fazla fark varsa isteği reddedin. Başarısız teslimatlar +1 dk, +10 dk, +1 saat sonra yeniden denenir.
Webhook teslimatı gelene kadar zaman damgası durumunu GET /v1/records/{id} ile sorgulayın.
Limitler ve kotalar uygulama planlı
Kotalar GET /v1/usage ile izlenebiliyor; aşağıdaki limitlerin zorlanması (429) MVP ile devreye girecek.
| Plan | İstek/dk (anahtar başına) | Mark + kayıt / ay |
|---|---|---|
| Free | 10 | 25 (API) + 50 web imzalama |
| Starter | 60 | 2.000 |
| Growth | 120 | 10.000 |
| Scale | 300 | 50.000 |
| Anahtarsız (IP başına) | 30 okuma/dk | — |
Kota dolduğunda istek durdurulur (429); otomatik aşım faturası yoktur.
Fiyatlar: fiyatlandırma. Gazeteciler, doğruluk kontrolü ekipleri ve bireysel
üreticiler için ücretsiz — bize yazın.
Hata kodları
Hata gövdesinde makine tarafından okunabilir bir error kodu ve çoğunlukla bir
message bulunur. v1'de tek tip bir zarf (type, code, request_id)
gelecek; kodlarınızı error/code alanına göre yazın, mesaj metnine göre değil.
| HTTP | error | Ne zaman |
|---|---|---|
| 400 | validation_error · invalid_json · invalid_hash · invalid_phash · invalid_pdq | Eksik/hatalı alan, bozuk JSON, yanlış hex uzunluğu |
| 401 | authentication_error (invalid_api_key · revoked_api_key) | Eksik, hatalı veya iptal edilmiş anahtar |
| 404 | not_found | Kayıt, toplu işlem veya route yok |
| 413 | file_too_large | Dosya 20 MB'tan büyük |
| 415 | unsupported_media_type | JPG/PNG dışında dosya |
| 429 planlı | rate_limit_exceeded · plan_quota_exceeded | Rate limit (Retry-After header'ına bakın) veya aylık kota |
| 503 | — | Geçici kesinti — exponential backoff ile yeniden deneyin |
OpenAPI spec
Çalışan route'ların OpenAPI 3.1 tanımı API'nin kendisinden servis edilir ve bu sayfadan önce gelir:
https://api.ozdna.com/v1/openapi.yaml
Ne iddia etmiyoruz
- AI tespiti yapmıyoruz. OriginDNA bir görselin AI ile üretilip üretilmediğini tahmin etmez; kökeni kayda alır ve doğrular.
- "Güvenilir Content Credentials" değil. ozDNA imzaları, C2PA Conformance Program'ı tamamlayana kadar resmi C2PA Verify aracında "bilinmeyen kaynak" olarak görünür. Bu yüzden kendi doğrulama sayfamızı ve bağımsız zaman damgası kanıtını sunuyoruz.
- Hukuki tavsiye değil. API, EU AI Act Madde 50 işaretleme yükümlülüğünü karşılamanıza yardımcı olacak bir altyapıdır; uyumun tamamını tek başına sağladığını iddia etmez.