API documentation

All endpoints are public (no edge auth) and live under /api/public/v1. Issuance requires a Bearer API key created in the admin dashboard.

POST /api/public/v1/credentials/issue

Validates the source URL against the allow-list, scrapes it with the configured parser, maps the extracted fields onto a Hovi credential template, and returns an OpenID4VCI credential_offer deeplink the wallet can open.

Headers

Authorization: Bearer vci_xxxxxxxx
Content-Type: application/json

Body

{
  "source_url": "https://mdl.rtad.gov.mm/detail?id=B/21624/17&nrc=95773514&off=Y&no=413033"
}

Response

{
  "offer_url": "openid-credential-offer://?credential_offer_uri=https%3A%2F%2F...",
  "credential_type": "MyanmarDriverLicense",
  "issued_at": "2025-01-01T00:00:00.000Z"
}

curl

curl -X POST https://your-issuer.example/api/public/v1/credentials/issue \
  -H "Authorization: Bearer vci_xxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{"source_url":"https://mdl.rtad.gov.mm/detail?id=B/21624/17&nrc=95773514&off=Y&no=413033"}'

Mobile flow

On mobile, take offer_url from the response and open it directly — the OS launches the user's wallet app and starts the OpenID4VCI flow with the upstream issuer.

// React Native
import { Linking } from "react-native";
await Linking.openURL(offerUrl);

// iOS / Android web
window.location.href = offerUrl;

On desktop or web, render offer_url as a QR code so the user can scan with their wallet.

GET /api/public/v1/allowed-sources

Returns the list of active allow-listed source domains.

Error codes

  • 401 missing_api_key / invalid_api_key
  • 400 invalid_request / invalid_url
  • 400 missing_required_attribute — scraped page is missing fields the template needs
  • 403 url_not_allowed — domain or path not on allow-list
  • 404 no_active_template_for_source — source has no Hovi template configured
  • 502 source_fetch_failed — could not fetch upstream URL
  • 502 issuer_failed — Hovi rejected the issuance request
  • 500 parse_failed — parser couldn't extract claims