Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 24 additions & 1 deletion catalogue/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,10 @@ ignores the v2 fields. **Always set `"version": 2` when using any v2 field.**
"license": "<SPDX id>",

"metadata_url": "https://<host>/apps/<id>/metadata.json",
"metadata_sha256": "<hex sha256 of metadata.json>"
"metadata_sha256": "<hex sha256 of metadata.json>",

"renamed_to": "<canonical id>, optional",
"hidden": false
}
]
}
Expand All @@ -58,6 +61,26 @@ fields stay required. `pilotctl` decodes the index directly into
`catalogueEntry` in `cmd/pilotctl/appstore_catalogue.go` — any field added
here must also land there.

### Renaming an app (`renamed_to` + `hidden`)

Both are optional v2 fields (**keep `"version": 2`**). To rename an app id
`old → new` without breaking existing installs, do NOT delete the old entry —
the daemon supervisor pins each installed app's publisher key from its
catalogue entry and **fail-closes (stops) an installed app whose id has no
pin**. Instead, replace the old entry's body with a **tombstone**: keep `id`
and `publisher` (so existing installs keep their pin and keep running), set
`"renamed_to": "<new id>"`, and set `"hidden": true`. Drop `bundle_url` /
`bundles` / `metadata_url` (the tombstone is not installable) and delete the old
`apps/<old-id>/` detail dir. Then add the full new entry under the new id and
re-sign.

A bundles-aware `pilotctl` then, for the old id: omits it from `catalogue`,
and on `install`/`view`/`call` prints a deprecation warning and routes to
`renamed_to`. `hidden` alone (without `renamed_to`) just omits an entry from the
listing while keeping it resolvable. Older clients ignore both fields (they see
a normal, pin-only entry). One hop only — a `renamed_to` that points at another
tombstone is a bug and is not chased.

`bundles` is the per-platform map keyed by `"os/arch"`. It is an **optional v2
field — keep `"version": 2`, do NOT bump to 3.** `loadCatalogue` fail-closes on
any version other than 1 or 2, so a version-3 catalogue is rejected wholesale by
Expand Down
230 changes: 230 additions & 0 deletions catalogue/apps/io.pilot.didit/metadata.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,230 @@
{
"schema_version": 1,
"id": "io.pilot.didit",
"display_name": "Didit",
"tagline": "One API for identity and fraud — KYC, liveness, face match, AML, and more, with a no-broker key you mint in one call.",
"description_md": "**Didit is one API for identity and fraud** — KYC/ID verification, liveness, face match, AML screening, proof of address, database validation, and email/phone OTP, wrapped as a single Pilot app. It fronts Didit's full platform: **hosted verification sessions**, reusable **workflows**, **users**, **billing**, **blocklists**, **questionnaires**, and **webhooks** — 40 methods in all.\n\n## Your own key, minted in one call — no email, no code\n\nThe hard part of using an identity provider is usually onboarding: signing up, confirming an email code, and wiring the key. This app removes all of it. **`didit.signup` takes no arguments** and returns a working key:\n\n- It signs a keyless request (your Pilot identity) to Pilot's Didit broker. The broker provisions a mailbox on Pilot infrastructure, registers a Didit account, reads Didit's one-time email code **server-side**, verifies it, and hands back your account's `api_key`.\n- The adapter caches `{email, api_key}` to `$APP/secrets.json`. From then on **every other method sends your key as `x-api-key` automatically** — you never see an inbox, a code, or the key unless you ask (`didit.account`).\n- **Idempotent:** the broker mints at most one Didit account per Pilot identity, so a repeat call — or a fresh install on another machine — returns the *same* account. The account is entirely **yours**: verifications bill to **your** Didit balance (top up with `didit.billing_topup`), and Pilot adds no markup. Each account includes Didit's **500 free full-KYC checks/month**; account creation, management, sessions CRUD, users, billing, blocklists, questionnaires and webhooks are all **free** — you pay only per verification you run.\n\n## The fast path\n\n1. `didit.signup {}` → your key is cached (one call, ~5s, no email).\n2. `didit.create_workflow` `{workflow_label:\"KYC\", features:[{feature:\"OCR\"},{feature:\"LIVENESS\"},{feature:\"FACE_MATCH\"}]}` → get `uuid`.\n3. `didit.create_session` `{workflow_id, vendor_data:\"user-123\"}` → send the user to the returned `url`.\n4. `didit.get_decision` `{session_id}` (or a webhook) → read the Approved/Declined result and extracted data.\n\n`didit.account` returns your provisioned email + key any time.\n\n## What each area does\n\n- **Sessions** — hosted flows where the user completes verification at a Didit URL, so you never handle document images: `create_session`, `get_decision`, `list_sessions`, `update_session_status` (approve/decline/resubmit), `delete_session`, `batch_delete_sessions`, `share_session` / `import_session` (B2B KYC reuse), `list_reviews`, `create_review`.\n- **Workflows** — templates built from an ordered `features` array (`OCR`, `LIVENESS`, `FACE_MATCH`, `AML`, `PROOF_OF_ADDRESS`, `PHONE_VERIFICATION`, `EMAIL_VERIFICATION`, `DATABASE_VALIDATION`, `IP_ANALYSIS`, `AGE_ESTIMATION`, `NFC`, `QUESTIONNAIRE`, `KYB_*`), each with an optional per-feature `config`: `create_workflow`, `list_workflows`, `get_workflow`, `update_workflow`, `delete_workflow`.\n- **Standalone checks (JSON, no session)** — `aml` (sanctions/PEP/adverse-media, $0.20), `database_validation` (gov sources, from $0.05).\n- **Contact** — `email_send`/`email_check` ($0.03) and `phone_send`/`phone_check` (from $0.03) OTP verification.\n- **Billing** — `billing_balance`, `billing_topup` (Stripe checkout URL).\n- **Governance** — `blocklist_*` (auto-flag repeat faces/docs/phones/emails), `questionnaire_*` (custom forms), `users_*` (people grouped by your `vendor_data`), `get_webhook`/`update_webhook` (set + rotate the HMAC secret programmatically).\n\n## Pricing\n\nPay-per-check on **your** Didit balance — no Pilot markup. See the full rate card in `didit.help`. Highlights: full KYC bundle **$0.33/check** (first **500/month free**), ID verification $0.15, passive liveness $0.10, face match $0.05, **face search free**, AML $0.20, PoA $0.20, email/phone from $0.03. Image-upload APIs (direct ID scan, liveness, face match, face search, age estimation, PoA) run through the **hosted session** flow rather than as direct methods.\n\n## Notes\n\n- The adapter dials exactly two hosts: Pilot's broker (`broker.pilotprotocol.network`) for the one-call `didit.signup`, and Didit (`verification.didit.me`) for every operational call with your cached key. It holds no shared secret; the broker signs you in, then steps out of the data path.\n- Plain request/response REST — no websockets, no async jobs. Rate limits: ~600 session-creates/min, 300/min per other method; the account OTP register is 5/IP/hour.\n- Errors surface verbatim: `401` (run `didit.signup` first), `403` (top up credits), `429` (back off).\n",
"vendor": {
"name": "Didit",
"url": "https://didit.me",
"contact": "hello@didit.me",
"publisher_pubkey": "ed25519:ii6QAc8qZB4NvbOXqnLfnv+OhPqepuK0BISAv9jM5x0="
},
"homepage": "https://didit.me",
"source_url": "https://github.com/didit-protocol/skills",
"license": "Proprietary",
"categories": [
"identity",
"verification",
"compliance",
"security",
"kyc"
],
"keywords": [
"kyc",
"identity",
"verification",
"aml",
"liveness",
"face-match",
"biometrics",
"proof-of-address",
"sanctions",
"pep",
"onboarding",
"fraud",
"kyb",
"otp",
"didit"
],
"size": {
"bundle_bytes": 5130067,
"installed_bytes": 9324763
},
"compat": {
"min_pilot_version": "1.0.0",
"runtimes": [
"go"
]
},
"methods": [
{
"name": "didit.signup",
"summary": "Get your own Didit API key in ONE call — no email, no code, no human step. This signs a keyless request to Pilot's Didit broker, which provisions a mailbox on Pilot infrastructure, registers a Didit account, reads the emailed one-time code server-side, verifies it, and returns your account's api_key. The adapter caches {email, api_key} to $APP/secrets.json, and from then on EVERY other didit.* method authenticates automatically (x-api-key) — you never handle the key or an inbox. Idempotent: the broker mints at most one account per Pilot identity, so a repeat call (or a fresh install) returns the SAME account. Run this ONCE before any other method. FREE — account creation costs nothing; you pay only per verification you run, and each account includes Didit's 500 free full-KYC checks/month. The account (email + key) is retrievable any time via didit.account. Takes no arguments."
},
{
"name": "didit.account",
"summary": "Retrieve your cached Didit account — the email the broker provisioned for you and your api_key — plus a signed_up flag. Local, instant, FREE (reads $APP/secrets.json; no backend call). Use it to confirm you're signed up or to read your key. If signed_up is false, call didit.signup first."
},
{
"name": "didit.billing_balance",
"summary": "Check your remaining Didit credit balance (and auto-refill settings). FREE. Returns {balance, auto_refill_enabled, auto_refill_amount, auto_refill_threshold}. Verifications draw down this balance; check it before a batch of checks."
},
{
"name": "didit.billing_topup",
"summary": "Add credit to your Didit balance. FREE call — returns a Stripe checkout URL (checkout_session_url) to present to the user; the charge happens on Stripe, not through Pilot."
},
{
"name": "didit.create_workflow",
"summary": "Create a verification workflow — the reusable template that defines which checks a hosted session runs, in order. FREE to create; you're billed per feature only when a session actually runs it. Returns {uuid} — pass it as workflow_id to didit.create_session. The v3 API takes a `features` ARRAY (in the order users complete them); each item is {feature, config?} where feature is one of OCR, NFC, LIVENESS, FACE_MATCH, PROOF_OF_ADDRESS, QUESTIONNAIRE, DOCUMENT_AI, PHONE_VERIFICATION, EMAIL_VERIFICATION, DATABASE_VALIDATION, AML, IP_ANALYSIS, AGE_ESTIMATION, KYB_REGISTRY, KYB_DOCUMENTS, KYB_KEY_PEOPLE. Example: [{\"feature\":\"OCR\"},{\"feature\":\"LIVENESS\",\"config\":{\"face_liveness_method\":\"PASSIVE\"}},{\"feature\":\"FACE_MATCH\"}]. The API uses a strict field whitelist — any undeclared key (e.g. workflow_type) is a 400. Max 50 workflows per account."
},
{
"name": "didit.list_workflows",
"summary": "List your verification workflows with their features and total_price. FREE."
},
{
"name": "didit.get_workflow",
"summary": "Get one workflow by id. FREE."
},
{
"name": "didit.update_workflow",
"summary": "Update a workflow (partial — send only the fields to change; same field set as create_workflow, e.g. a replacement `features` array, workflow_label, status, is_default). FREE."
},
{
"name": "didit.delete_workflow",
"summary": "Delete a workflow. FREE. Existing sessions are unaffected. Returns 204."
},
{
"name": "didit.create_session",
"summary": "Start a hosted verification session for a user and get a URL to send them to. This is Didit's recommended path for ID/liveness/face-match/AML/PoA/etc. — the user completes everything at the hosted URL, so you never handle document images yourself. COST is the sum of the features the workflow enables (e.g. a full KYC bundle ≈ $0.33/check; 500 full-KYC checks/month are free), charged to your Didit balance when the session runs. Returns {session_id, session_token, url, status}. Poll didit.get_decision or set a webhook for the result. Nested objects (contact_details, expected_details) are passed as JSON objects."
},
{
"name": "didit.get_decision",
"summary": "Get the full decision and extracted data for a session — status plus id_verifications, liveness_checks, face_matches, aml_screenings, phone/email verifications, poa_verifications, database_validations, ip_analyses, and reviews. FREE (reading results). Image URLs in the response expire after 60 minutes. Statuses: Not Started | In Progress | In Review | Approved | Declined | Abandoned | Expired | Resubmitted."
},
{
"name": "didit.list_sessions",
"summary": "List/filter your sessions (paginated). FREE."
},
{
"name": "didit.update_session_status",
"summary": "Manually override a session's status (approve/decline/resubmit) — the programmatic-review action. FREE. For Resubmitted, pass nodes_to_resubmit; the session must be Declined, In Review, or Abandoned."
},
{
"name": "didit.delete_session",
"summary": "Permanently delete a session and all its data. FREE. Returns 204."
},
{
"name": "didit.batch_delete_sessions",
"summary": "Delete many sessions at once by number (or all). FREE."
},
{
"name": "didit.share_session",
"summary": "Generate a share_token so a partner can import a finished session (B2B KYC reuse). FREE. Works only for finished sessions."
},
{
"name": "didit.import_session",
"summary": "Import a session shared by a partner via its share_token. FREE."
},
{
"name": "didit.list_reviews",
"summary": "List the manual-review activity for a session (status changes, notes). FREE."
},
{
"name": "didit.create_review",
"summary": "Add a manual review decision to a session (Approved/Declined/In Review). FREE."
},
{
"name": "didit.aml",
"summary": "Screen a person or company against sanctions, PEP, and adverse-media watchlists (standalone, no session). COST $0.20/check on your Didit balance. Returns matches with scores and categories."
},
{
"name": "didit.database_validation",
"summary": "Cross-check identity fields against government / authoritative databases (standalone). COST from $0.05/check (1x1, single source) to $0.30 (2x2, two-source cross-validation); varies by country/source. Covers 1,000+ sources across 18+ countries."
},
{
"name": "didit.email_send",
"summary": "Send a one-time verification code to an email address. Part of email verification ($0.03 per completed verification, charged to your Didit balance)."
},
{
"name": "didit.email_check",
"summary": "Verify the email OTP the user received. Completes an email verification ($0.03)."
},
{
"name": "didit.phone_send",
"summary": "Send a one-time code by SMS / WhatsApp / Telegram. Part of phone verification (from $0.03 per completed verification, varies by channel)."
},
{
"name": "didit.phone_check",
"summary": "Verify the phone OTP the user received. Completes a phone verification (from $0.03)."
},
{
"name": "didit.blocklist_add",
"summary": "Add a session's face/document/phone/email to your blocklist so future matches auto-flag (FACE_IN_BLOCKLIST, etc.). FREE."
},
{
"name": "didit.blocklist_remove",
"summary": "Remove items from your blocklist. FREE."
},
{
"name": "didit.blocklist_list",
"summary": "List your blocklisted items. FREE."
},
{
"name": "didit.create_questionnaire",
"summary": "Create a custom form (7 element types) to attach to a questionnaire_verification workflow. FREE."
},
{
"name": "didit.list_questionnaires",
"summary": "List your questionnaires. FREE."
},
{
"name": "didit.get_questionnaire",
"summary": "Get one questionnaire. FREE."
},
{
"name": "didit.update_questionnaire",
"summary": "Update a questionnaire (partial). FREE."
},
{
"name": "didit.delete_questionnaire",
"summary": "Delete a questionnaire. FREE. Returns 204."
},
{
"name": "didit.list_users",
"summary": "List verified individuals (grouped by vendor_data) with status and session counts. FREE."
},
{
"name": "didit.get_user",
"summary": "Get one user by your vendor_data. FREE."
},
{
"name": "didit.update_user",
"summary": "Update a user's display name / manual status / metadata. FREE."
},
{
"name": "didit.batch_delete_users",
"summary": "Delete many users by vendor_data (or all). FREE."
},
{
"name": "didit.get_webhook",
"summary": "Get your webhook configuration (url, version, HMAC secret_shared_key, capture_method). FREE."
},
{
"name": "didit.update_webhook",
"summary": "Set/rotate your webhook config programmatically — no console needed. FREE."
},
{
"name": "didit.help",
"summary": "Discovery: every method with params, kind, and latency class."
}
],
"changelog": [
{
"version": "1.0.0",
"notes": [
"Released v1.0.0"
]
}
],
"links": [
{
"label": "Source",
"url": "https://github.com/didit-protocol/skills"
},
{
"label": "Website",
"url": "https://didit.me"
}
]
}
Loading
Loading