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.
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.
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.
# 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.
# 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.
# 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.
# Successful credit decision response:
{
"success": true,
"approved": true,
"scoreBand": "gold",
"maxLtvBps": 6500,
"riskPremiumBps": 150,
"validAt": "2025-06-07T12:00:10.000Z"
}Complete Example
All four steps in a single TypeScript function using the DID flow:
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.