v0.1.0-beta

Quick Start

Get a private credit check from Veil in four API calls. Submit a behavioral event, get a challenge, have the user sign with their CKB wallet, and read the result. No smart contract deployment needed on your end.

Time to complete
This guide takes about 5 minutes to follow. For production patterns with full error handling, polling utilities, and TypeScript types, see the Integration Guide.

What You Need

  • VEIL_API_URL — the Veil backend endpoint provided during issuer onboarding. Current preview endpoint: https://api.13-61-145-21.sslip.io/api/v1.
  • issuerPk— your app's issuer key, assigned when the Veil admin approves your registration.
  • userPk— the user's Veil Key from the dashboard. Protocols use this to submit behavioral events keyed to that user.
  • User's Veil DID (did:veil:0x…) — the user obtains this from the dashboard after minting their identity pass. This is the only identifier you need to request a credit decision. Users can share it freely.
Tip
You do not need to deploy any smart contract or run a Midnight node. The Veil backend handles proof generation and Midnight transactions entirely on your behalf.

1. Submit a User Event

Tell Veil about something the user did in your app. In this example, a user made an on-time loan repayment. Submit events in real time using the user's Veil Key. The backend queues the event and processes it on Midnight.

bashPOST /scoring-events/repayments
# Submit a repayment event for a user
curl -X POST "$VEIL_API_URL/scoring-events/repayments" \
  -H "Content-Type: application/json" \
  -d '{
    "userPk": "<user-veil-key-hex>",
    "issuerPk": "<your-issuer-pk-hex>",
    "paidOnTimeFlag": 1,
    "amountWeight": 1000,
    "eventEpoch": 3,
    "eventId": "<optional-unique-event-id>"
  }'
# → { "success": true, "job": { "id": "job_7f3a91b2", "status": "pending", ... } }

All four event types follow the same pattern: POST /scoring-events/repayments, /liquidations, /protocol-usage, and /debt-states. See the Scoring Model for field details. Each event returns a job ID — use GET /jobs/:id to poll until the event settles on-chain.

2. Obtain a Challenge

Before requesting a credit decision, obtain a one-time challenge from the backend. Challenges expire in 60 seconds and prevent replay attacks. Request a fresh challenge each time — never cache or reuse one.

bashPOST /challenges
# Obtain a one-time challenge (expires in 60 seconds)
curl -X POST "$VEIL_API_URL/challenges"
# → { "challenge": "0x1a2b3c4d...", "challengeExpiresAt": 1749294060000 }

3. Request a Credit Check

Build a short authorization message using the user's Veil DID and the challenge, have their CKB wallet sign it, then submit the signed request. The backend verifies the user's identity pass and returns a credit decision.

Message to sign (DID flow)
Veil credit decision authorization did:{veilDid} challenge:{challengeHex} verificationMethod:{veilDid}#ckb-owner-1 registryVersion:1 purpose:credit-decision
bashPOST /credit-decisions
# The user's Veil DID — they share it with you or you resolve it
VEIL_DID="did:veil:0x..."
CHALLENGE="0x1a2b3c4d..."

# Build the message for the user's CKB wallet to sign:
# Veil credit decision authorization
# did:<VEIL_DID>
# challenge:<CHALLENGE>
# verificationMethod:<VEIL_DID>#ckb-owner-1
# registryVersion:1
# purpose:credit-decision

# After the user signs the message with their CKB wallet:
curl -X POST "$VEIL_API_URL/credit-decisions" \
  -H "Content-Type: application/json" \
  -d '{
    "did": "'"$VEIL_DID"'",
    "challenge": "'"$CHALLENGE"'",
    "authorization": {
      "signature": "0x...",
      "identity": "<signing-key-hex>",
      "signType": "ckbSecp256k1",
      "verificationMethod": "'"$VEIL_DID"'#ckb-owner-1"
    }
  }'

4. Read the Result

The response tells you the user's trust tier and whether they are approved for the action you are gating. /credit-decisions is synchronous — no polling needed.

jsonDecision response
# Successful credit decision response:
{
  "success": true,
  "approved": true,
  "scoreBand": "gold",
  "maxLtvBps": 6500,
  "riskPremiumBps": 150,
  "validAt": "2025-06-07T12:00:10.000Z"
}
approved
true if the user has a valid identity pass, a registered DID, and meets the current policy.
scoreBand
Trust tier: unranked · bronze · silver · gold · platinum
maxLtvBps
Maximum loan-to-value in basis points. 6500 = 65% LTV. Use this directly in your lending logic.
validAt
ISO 8601 timestamp. The decision reflects the user's state at this exact moment.

Complete Example

All four steps in a single TypeScript function using the DID flow:

typescriptquick-start.ts
const VEIL = process.env.VEIL_API_URL ?? 'https://api.13-61-145-21.sslip.io/api/v1';

interface CkbSigner {
  signMessage(message: string): Promise<{ signature: string; identity: string; signType: string }>;
}

async function quickIntegration(
  userPk: string,      // user's Veil public key — for submitting behavioral events
  veilDid: string,     // user's Veil DID (did:veil:0x...) — for credit decisions
  issuerPk: string,
  ckbSigner: CkbSigner,
) {
  // 1. Submit a repayment event
  const { job } = await fetch(`${VEIL}/scoring-events/repayments`, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
      userPk,
      issuerPk,
      paidOnTimeFlag: 1,
      amountWeight: 1000,
      eventEpoch: 3,
    }),
  }).then((r) => r.json());

  // Optionally poll the event job (not required before requesting a decision)
  await pollJob(job.id);

  // 2. Obtain a fresh challenge
  const { challenge } = await fetch(`${VEIL}/challenges`, { method: 'POST' })
    .then((r) => r.json());

  // 3. Build the DID authorization message and have the user sign it
  const verificationMethod = `${veilDid}#ckb-owner-1`;
  const message = [
    'Veil credit decision authorization',
    `did:${veilDid}`,
    `challenge:${challenge}`,
    `verificationMethod:${verificationMethod}`,
    'registryVersion:1',
    'purpose:credit-decision',
  ].join('\n');
  const authorization = await ckbSigner.signMessage(message);

  // 4. Request the credit decision
  const decision = await fetch(`${VEIL}/credit-decisions`, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
      did: veilDid,
      challenge,
      authorization: { ...authorization, verificationMethod },
    }),
  }).then((r) => r.json());

  // 5. Act on the result
  if (decision.approved) {
    console.log(`Tier: ${decision.scoreBand}, Max LTV: ${decision.maxLtvBps / 100}%`);
    grantPrivilegedAccess(decision.scoreBand);
  }

  return decision;
}

async function pollJob(jobId: string): Promise<void> {
  while (true) {
    const { job } = await fetch(`${VEIL}/jobs/${jobId}`).then((r) => r.json());
    if (job.status === 'succeeded') return;
    if (job.status === 'failed') throw new Error(job.error ?? 'Job failed');
    await new Promise((r) => setTimeout(r, 3000));
  }
}

declare function grantPrivilegedAccess(band: string): void;

Next Steps

  • Integration Guide — production-ready patterns with full error handling, retry logic, and all four event types.
  • API Reference — complete reference for all endpoints, including DID resolution, request schemas, and response types.
  • Scoring Model — understand the scoring formula and how behavioral signals map to trust tiers.
  • Dashboard Guide — walk users through creating their Veil ID, minting their identity pass, and sharing their Veil DID.