Identity Verification
Verify Identity
Register a wallet on the identity register and record the customer as verified
POST
/
customers
/
api
/
identity
/
verify
curl -X POST "https://api.trusset.org/customers/api/identity/verify" \
-H "Content-Type: application/json" \
-H "X-API-Key: trusset_your_key_here" \
-d '{
"walletAddress": "0xaBc123DEf456789012345678901234567890aBCD",
"country": "DE",
"investorType": "PROFESSIONAL",
"softExpiryDays": 365,
"hardExpiryDays": 730
}'
const url = 'https://api.trusset.org/customers/api/identity/verify';
const headers = {
'Content-Type': 'application/json',
'X-API-Key': 'trusset_your_key_here'
};
const request = {
walletAddress: '0xaBc123DEf456789012345678901234567890aBCD',
country: 'DE',
investorType: 'PROFESSIONAL',
softExpiryDays: 365,
hardExpiryDays: 730
};
const res = await fetch(url, { method: 'POST', headers, body: JSON.stringify(request) });
const { data } = await res.json();
const tx = await wallet.sendTransaction(data.transaction);
await tx.wait();
await fetch(url, {
method: 'POST',
headers,
body: JSON.stringify({ ...request, kycHash: data.kycHash, txHash: tx.hash })
});
import { ethers } from 'ethers';
const base = 'https://api.trusset.org/customers/api/identity';
const headers = {
'Content-Type': 'application/json',
'X-API-Key': 'trusset_your_key_here'
};
const post = async (path: string, body: Record<string, unknown>) => {
const res = await fetch(`${base}${path}`, { method: 'POST', headers, body: JSON.stringify(body) });
const json = await res.json();
if (!json.success) throw new Error(`${json.error.code}: ${json.error.message}`);
return json.data;
};
const request: Record<string, unknown> = {
walletAddress: '0xaBc123DEf456789012345678901234567890aBCD',
country: 'DE',
investorType: 'PROFESSIONAL',
signerAddress: await wallet.getAddress()
};
let plan = await post('/verify', request);
request.kycHash = plan.kycHash;
if (plan.phase === 'DEPLOY_IDENTITY') {
for (const step of plan.steps) {
const tx = await wallet.sendTransaction({ data: step.transaction.data });
await tx.wait();
await post('/onchainid/confirm', { walletAddress: step.walletAddress, txHash: tx.hash });
}
plan = await post('/verify', request);
}
if (plan.phase === 'SIGN_CLAIMS') {
const claimSignatures = [];
for (const message of plan.messages) {
const signature = await wallet.signMessage(ethers.utils.arrayify(message.messageHash));
claimSignatures.push({ walletAddress: message.walletAddress, signature });
}
plan = await post('/verify', { ...request, claimSignatures });
}
const hashes: string[] = [];
for (const step of plan.steps) {
const tx = await wallet.sendTransaction(step.transaction);
await tx.wait();
hashes.push(tx.hash);
}
const confirmed = await post('/verify', { ...request, txHash: hashes[plan.confirmStepIndex] });
{
"success": true,
"data": {
"action": "SIGN_TRANSACTION",
"transaction": {
"to": "0xEf244c4Bd4d71Bd9A30EDF6Edef7CCC72642291a",
"data": "0x05bd97c8..."
},
"functionName": "verifyIdentity",
"description": "Verify 0xaBc123DEf456789012345678901234567890aBCD on the identity registry",
"registerKind": "TRUSSET",
"walletAddress": "0xaBc123DEf456789012345678901234567890aBCD",
"kycHash": "0xffb1f8c219725bd62205597cd7247c85cafeca20ac0a59f8ec6a7b979eaf672c",
"investorType": 2,
"softExpiry": 1822305600,
"hardExpiry": 1853841600,
"operation": "verify",
"confirmWith": {
"endpoint": "POST /customers/api/identity/verify",
"field": "txHash"
}
},
"error": null,
"metadata": {
"requestId": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-09-30T12:00:00.000Z"
}
}
{
"success": true,
"data": {
"register": "0x4C2b1a0f9E8d7c6B5a4938271605F4e3D2c1B0A9",
"registerKind": "TREX",
"signerAddress": "0x51A4F0c3B2e8d7A6c5B4e3f2A1d0c9b8e7f6A5D4",
"claimSigners": ["0x51A4F0c3B2e8d7A6c5B4e3f2A1d0c9b8e7f6A5D4"],
"action": "SIGN_MESSAGES",
"phase": "SIGN_CLAIMS",
"messages": [
{
"walletAddress": "0xaBc123DEf456789012345678901234567890aBCD",
"identity": "0x7d3e2c1b0A9F8E7D6C5b4A39281706f5E4d3C2b1",
"topic": "109015029463275868683647211611771110530627425002451180747115609001006883683411",
"claimIssuer": "0x9a8B7c6d5e4f30211203F4E5d6c7B8a998877665",
"data": "0xffb1f8c219725bd62205597cd7247c85cafeca20ac0a59f8ec6a7b979eaf672c",
"messageHash": "0x62db268a9c0c0485b5de02f7a6d1a266d17227af2a12a1c400581e6d726bc359",
"scheme": "eip191",
"description": "Sign keccak256(abi.encode(identity, topic, data)) as an EIP-191 personal message with a wallet that holds a CLAIM key on 0x9a8B7c6d5e4f30211203F4E5d6c7B8a998877665",
"claimType": "KYC"
}
],
"identities": {
"0xaBc123DEf456789012345678901234567890aBCD": "0x7d3e2c1b0A9F8E7D6C5b4A39281706f5E4d3C2b1"
},
"claimIssuer": "0x9a8B7c6d5e4f30211203F4E5d6c7B8a998877665",
"claimTopic": "109015029463275868683647211611771110530627425002451180747115609001006883683411",
"next": "Sign every messageHash as an EIP-191 personal message with a wallet that holds a CLAIM key on the trusted issuer, then repeat this call with claimSignatures: [{ walletAddress, signature }].",
"walletAddress": "0xaBc123DEf456789012345678901234567890aBCD",
"kycHash": "0xffb1f8c219725bd62205597cd7247c85cafeca20ac0a59f8ec6a7b979eaf672c",
"investorType": 2,
"softExpiry": 0,
"hardExpiry": 0,
"operation": "verify",
"confirmWith": {
"endpoint": "POST /customers/api/identity/verify",
"field": "txHash"
}
},
"error": null,
"metadata": {
"requestId": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-09-30T12:00:00.000Z"
}
}
{
"success": true,
"data": {
"register": "0x4C2b1a0f9E8d7c6B5a4938271605F4e3D2c1B0A9",
"registerKind": "TREX",
"signerAddress": "0x51A4F0c3B2e8d7A6c5B4e3f2A1d0c9b8e7f6A5D4",
"claimSigners": ["0x51A4F0c3B2e8d7A6c5B4e3f2A1d0c9b8e7f6A5D4"],
"action": "SIGN_TRANSACTIONS",
"phase": "REGISTER",
"steps": [
{
"action": "SIGN_TRANSACTION",
"stepType": "claim",
"walletAddress": "0xaBc123DEf456789012345678901234567890aBCD",
"identity": "0x7d3e2c1b0A9F8E7D6C5b4A39281706f5E4d3C2b1",
"transaction": {
"to": "0x7d3e2c1b0A9F8E7D6C5b4A39281706f5E4d3C2b1",
"data": "0xb1a34e0d..."
},
"functionName": "addClaim",
"description": "Add the KYC claim of 0xaBc123DEf456789012345678901234567890aBCD to its ONCHAINID 0x7d3e2c1b0A9F8E7D6C5b4A39281706f5E4d3C2b1",
"claimType": "KYC",
"dataHash": "0xffb1f8c219725bd62205597cd7247c85cafeca20ac0a59f8ec6a7b979eaf672c"
},
{
"action": "SIGN_TRANSACTION",
"stepType": "verify",
"walletAddress": "0xaBc123DEf456789012345678901234567890aBCD",
"transaction": {
"to": "0x4C2b1a0f9E8d7c6B5a4938271605F4e3D2c1B0A9",
"data": "0x454a03e0..."
},
"functionName": "registerIdentity",
"description": "Register 0xaBc123DEf456789012345678901234567890aBCD on the identity register 0x4C2b1a0f9E8d7c6B5a4938271605F4e3D2c1B0A9",
"ids": []
}
],
"confirmStepIndex": 1,
"identities": {
"0xaBc123DEf456789012345678901234567890aBCD": "0x7d3e2c1b0A9F8E7D6C5b4A39281706f5E4d3C2b1"
},
"claimIssuer": "0x9a8B7c6d5e4f30211203F4E5d6c7B8a998877665",
"claimTopic": "109015029463275868683647211611771110530627425002451180747115609001006883683411",
"registerStep": "registerIdentity",
"description": "Verify 0xaBc123DEf456789012345678901234567890aBCD on the identity registry",
"walletAddress": "0xaBc123DEf456789012345678901234567890aBCD",
"kycHash": "0xffb1f8c219725bd62205597cd7247c85cafeca20ac0a59f8ec6a7b979eaf672c",
"investorType": 2,
"softExpiry": 0,
"hardExpiry": 0,
"operation": "verify",
"confirmWith": {
"endpoint": "POST /customers/api/identity/verify",
"field": "txHash"
}
},
"error": null,
"metadata": {
"requestId": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-09-30T12:00:00.000Z"
}
}
{
"success": true,
"data": {
"walletAddress": "0xaBc123DEf456789012345678901234567890aBCD",
"status": "verified",
"operation": "verify",
"txHash": "0xdd729a9ad03451b7129c87e78e11b76bcd962d79d01e79ca69d95bb63342fdb9",
"gasUsed": "84521",
"kycHash": "0xffb1f8c219725bd62205597cd7247c85cafeca20ac0a59f8ec6a7b979eaf672c"
},
"error": null,
"metadata": {
"requestId": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-09-30T12:05:00.000Z"
}
}
{
"success": false,
"data": null,
"error": {
"code": "NOT_VERIFIED_ON_REGISTER",
"message": "The register holds no identity for this wallet yet. Register it, then confirm again."
},
"metadata": {
"requestId": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-09-30T12:05:00.000Z"
}
}
Puts a wallet on the identity register your instance writes to. Once you confirm the mined transaction, the customer record is created or updated as
What you sign depends on the register. Every calldata response names it in
The API refuses up front with
verified. Nothing is broadcast for you: every call returns what to sign.
investorType must name a preset or one of your verification profiles. Any other value, INSTITUTIONAL, NONE or a number included, is refused with 400 INVALID_INVESTOR_TYPE on both legs, and the message lists the values your instance accepts.registerKind.
registerKind | Register | The calldata leg returns |
|---|---|---|
TRUSSET | The Trusset ID Register, or your own deployment of the same contract | One transaction, verifyIdentity or updateIdentity |
TREX | Your own ERC-3643 (T-REX) identity registry, with ONCHAINID identities | A plan in up to three phases, one call per phase |
Body Parameters
string
required
The wallet to register. Checksummed or lowercase. A mixed-case address with a wrong checksum is refused.
string
required
ISO 3166-1 code as alpha-2 (
DE), alpha-3 (DEU) or numeric (276). Stored in alpha-3 form. An ERC-3643 register also receives it, as the numeric code.Refused with COUNTRY_BLOCKED when it is on the blocked list of the verification profile named after investorType.string
default:"RETAIL"
RETAIL, PROFESSIONAL or ELIGIBLE_COUNTERPARTY in any letter case, or the key of one of your custom verification profiles, also in any letter case. A custom key registers with that profile’s on-chain investor type. Omitted, null or an empty string reads as RETAIL. A disabled profile is refused with INVALID_INVESTOR_TYPE on the build call unless the wallet’s customer record is already under it, so a customer of a disabled profile can still be verified again. The confirm call records what was signed. Anything else, a number included, is refused with INVALID_INVESTOR_TYPE.A Trusset register stores the type on chain as 1, 2 or 3. An ERC-3643 register has no investor type, so there it is kept on the customer record only. The record keeps the profile key as the profile spells it, so professional is stored as PROFESSIONAL.integer
Days until the identity is soft-expired.
0 or omitted sets none. Trusset register only: an ERC-3643 register does not store it.integer
Days until the identity is hard-expired and stops counting as verified.
0 or omitted sets none. Trusset register only.The API does not check either expiry. A Trusset register reverts the transaction when the soft expiry is not earlier than the hard expiry, or when either lies in the past.string
The KYC hash to register, as 32-byte hex (
0x and 64 hex characters). Stored lowercased. For a KYC proof bundle this is its Merkle root. On an ERC-3643 register it becomes the data of the KYC claim.object
Data to derive the hash from when
kycHash is omitted: the keccak256 of its JSON serialisation. With neither, the hash covers the wallet, the country and the current time, so it changes on every call.array
ERC-3643 only. The signatures from the
SIGN_CLAIMS phase, as [{ walletAddress, signature }]. An entry may add claimType, such as KYC.string
ERC-3643 only. A single claim signature for
walletAddress, instead of claimSignatures.object
ERC-3643 only. An ONCHAINID you deployed with your own tooling, keyed by wallet:
{ "<wallet>": "<onchainid>" }. It is used when the register holds no identity for the wallet and the contract answers as an ONCHAINID. Otherwise it is ignored.string
ERC-3643 only. The wallet that will sign the claim. Refused with
CLAIM_SIGNER_NOT_TRUSTED when it holds no claim key on the register’s trusted issuer. Defaults to the first of your instance’s wallets, primary first, that holds one.It also becomes the management key of any ONCHAINID the plan deploys. Name a wallet registered to your instance, because Confirm ONCHAINID refuses any other management key.string
The hash of the mined transaction. Send it with the same body to confirm. On an ERC-3643 register, send the hash of
steps[confirmStepIndex]. Omit it to receive the calldata or the next phase.Response Fields
object
Show Calldata leg, both registers
Show Calldata leg, both registers
string
TRUSSET or TREX.string
The wallet, checksummed.
string
The hash this call used. Send it back on every later call for the same registration.
integer
1, 2 or 3. Not written to an ERC-3643 register.integer
Unix seconds, or
0. Not written to an ERC-3643 register.integer
Unix seconds, or
0. Not written to an ERC-3643 register.string
update when the wallet already holds an unrevoked identity on a Trusset register, or is already on an ERC-3643 register. verify otherwise.object
{ endpoint: "POST /customers/api/identity/verify", field: "txHash" }. It names this endpoint in every phase, including DEPLOY_IDENTITY, where the deployments are confirmed elsewhere.Show Calldata leg, Trusset register
Show Calldata leg, Trusset register
Show Calldata leg, ERC-3643 register
Show Calldata leg, ERC-3643 register
string
SIGN_TRANSACTIONS in DEPLOY_IDENTITY and REGISTER, SIGN_MESSAGES in SIGN_CLAIMS.string
DEPLOY_IDENTITY, SIGN_CLAIMS or REGISTER.string
The identity registry address.
string
The wallet the plan was built for. It holds a claim key on
claimIssuer.string[]
Among
signerAddress and your instance’s wallets, those that hold a claim key on claimIssuer.string
The trusted issuer contract whose claim key signs the claim.
string
The claim topic the register requires, as a decimal string.
object
The ONCHAINID of each wallet, where one is known.
array
DEPLOY_IDENTITY and REGISTER only. Transactions to send in order, each with stepType (deployIdentity, claim, verify or country), walletAddress, transaction, functionName and description. A deployment’s transaction is { to: null, data, create: true }, and the step adds managementKey and implementationAuthority.array
SIGN_CLAIMS only. One per claim, with walletAddress, identity, topic, claimIssuer, data, messageHash, scheme (eip191), claimType and description.integer
REGISTER only. The step whose hash confirms the registration.string
REGISTER only. registerIdentity when the wallet is new to the register, null when it is already on it.string
DEPLOY_IDENTITY and SIGN_CLAIMS only. What to do before calling again.Show Confirm leg
Show Confirm leg
string
The wallet, checksummed.
string
Always
verified.string
verify or update, from the mined function. On an ERC-3643 register it is verify for every step the plan builds.string
The confirmed transaction.
string
Gas used.
string
Read from the chain, not from your request: the mined hash on a Trusset register, the KYC claim’s data on an ERC-3643 register.
With neither
kycHash nor kycData, each call derives a new hash from the current time. Send the kycHash from the first response on every later call. On an ERC-3643 register this is required: the claim signature covers the hash, and a new hash makes it fail with CLAIM_SIGNER_NOT_TRUSTED.Verify on a Trusset register
Call once withouttxHash and send transaction from a wallet that holds KYC_PROVIDER_ROLE on the register. Then send the same body again with txHash added.
The endpoint picks updateIdentity when the wallet already holds an identity that is not revoked, expired ones included. It picks verifyIdentity when the wallet has none or was revoked. If the register cannot be read, it falls back to verifyIdentity, which reverts for a wallet that already holds an unrevoked identity.
When the wallet already holds an active identity, the calldata calls updateIdentity instead. On the Trusset ID Register, which instances without a register of their own share, that update is refused with 403 IDENTITY_HELD_BY_ANOTHER_ISSUER when the identity was registered by a wallet that is not one of your instance’s and has not expired. Before handing out calldata, the API checks that one of your instance’s wallets holds KYC_PROVIDER_ROLE. It refuses with KYC_PROVIDER_ROLE_MISSING only when it can read every wallet and none holds the role. With no wallet registered, or when the read fails, it lets the call through. The verdict is cached for 60 seconds, so a refusal can outlast a new role grant by up to a minute.
Verify on an ERC-3643 register
Each call answers the next phase. Call again with what the previous phase produced untilphase is REGISTER, carrying the same kycHash throughout.
1
Deploy an ONCHAINID (DEPLOY_IDENTITY)
Returned when the wallet has no ONCHAINID. Send each step’s
transaction as a contract creation, then confirm each deployment with Confirm ONCHAINID so it is recorded. Confirming a deployment here returns TX_WRONG_TARGET, whatever confirmWith says.You can deploy ahead of time with Deploy ONCHAINID, or pass an ONCHAINID you deployed yourself in identities.2
Sign the claim (SIGN_CLAIMS)
Sign each
messageHash as an EIP-191 personal message over its 32 bytes. Use a wallet that holds a CLAIM key on claimIssuer, such as one in claimSigners. Call again with claimSignatures: [{ walletAddress, signature }].3
Register (REGISTER)
Send
steps in order. The claim step adds the claim to the wallet’s ONCHAINID. The verify step registers a new wallet, and a country step corrects the country of a wallet already on the register. Confirm with the hash of steps[confirmStepIndex].| Transaction or signature | Who must hold what |
|---|---|
| ONCHAINID deployment | Any wallet can send it. The management key is fixed in the deployment data, and is signerAddress. |
| Claim signature | A CLAIM key (purpose 3) on claimIssuer. |
addClaim on the ONCHAINID | A management or claim key of that ONCHAINID. For one the plan deployed, that is signerAddress. |
registerIdentity, updateCountry | An agent of the register. |
REGISTRY_AGENT_MISSING when none of your instance’s wallets is an agent, and with CLAIM_SIGNER_MISSING when none holds a claim key. It does not check that the agent and signerAddress are the same wallet.
An ERC-3643 register stores no investor type and no expiry. investorType reaches the customer record only, and the two expiry fields are not stored anywhere.
The ERC-3643 example sends everything from one wallet that is both an agent of the register and a claim signer. Passing its address as signerAddress makes it the management key of any ONCHAINID it deploys, so it can also send the claim step.
Confirm
The confirm leg reads the mined transaction and refuses it before writing anything when:- it is not mined, reverted, or is not a register or ONCHAINID write this endpoint confirms (
TX_WRONG_TARGET,TX_WRONG_FUNCTION) - it targets a different wallet, or an ONCHAINID that is not this wallet’s (
TX_TARGET_MISMATCH) - on the Trusset ID Register, it was not signed by one of your instance’s verified wallets (
UNAUTHORIZED_SIGNER). On your own register the signer is logged, not checked - on a Trusset register, the mined investor type differs from this request’s (
TX_INVESTOR_TYPE_MISMATCH) - on an ERC-3643 register, the register does not yet report the wallet as verified (
NOT_VERIFIED_ON_REGISTER)
country, the profile key investorType resolved to, status: "verified", verifiedAt and the kycHash read from the chain, and records the write in your instance’s verification history as a VERIFY_DIRECT entry with the operation (verify or update), the mined investor type, the transaction and its signer. A reclassification through this endpoint therefore shows in the history like one made in the issuer app. When the profile investorType names has an eligibility register, the wallet’s admission to it is queued for your wallet to sign. See Eligibility registers.
Confirming also settles any pending verification for the same wallet. Its row is marked confirmed with this transaction, so the queue does not ask for the signature again.
curl -X POST "https://api.trusset.org/customers/api/identity/verify" \
-H "Content-Type: application/json" \
-H "X-API-Key: trusset_your_key_here" \
-d '{
"walletAddress": "0xaBc123DEf456789012345678901234567890aBCD",
"country": "DE",
"investorType": "PROFESSIONAL",
"softExpiryDays": 365,
"hardExpiryDays": 730
}'
const url = 'https://api.trusset.org/customers/api/identity/verify';
const headers = {
'Content-Type': 'application/json',
'X-API-Key': 'trusset_your_key_here'
};
const request = {
walletAddress: '0xaBc123DEf456789012345678901234567890aBCD',
country: 'DE',
investorType: 'PROFESSIONAL',
softExpiryDays: 365,
hardExpiryDays: 730
};
const res = await fetch(url, { method: 'POST', headers, body: JSON.stringify(request) });
const { data } = await res.json();
const tx = await wallet.sendTransaction(data.transaction);
await tx.wait();
await fetch(url, {
method: 'POST',
headers,
body: JSON.stringify({ ...request, kycHash: data.kycHash, txHash: tx.hash })
});
import { ethers } from 'ethers';
const base = 'https://api.trusset.org/customers/api/identity';
const headers = {
'Content-Type': 'application/json',
'X-API-Key': 'trusset_your_key_here'
};
const post = async (path: string, body: Record<string, unknown>) => {
const res = await fetch(`${base}${path}`, { method: 'POST', headers, body: JSON.stringify(body) });
const json = await res.json();
if (!json.success) throw new Error(`${json.error.code}: ${json.error.message}`);
return json.data;
};
const request: Record<string, unknown> = {
walletAddress: '0xaBc123DEf456789012345678901234567890aBCD',
country: 'DE',
investorType: 'PROFESSIONAL',
signerAddress: await wallet.getAddress()
};
let plan = await post('/verify', request);
request.kycHash = plan.kycHash;
if (plan.phase === 'DEPLOY_IDENTITY') {
for (const step of plan.steps) {
const tx = await wallet.sendTransaction({ data: step.transaction.data });
await tx.wait();
await post('/onchainid/confirm', { walletAddress: step.walletAddress, txHash: tx.hash });
}
plan = await post('/verify', request);
}
if (plan.phase === 'SIGN_CLAIMS') {
const claimSignatures = [];
for (const message of plan.messages) {
const signature = await wallet.signMessage(ethers.utils.arrayify(message.messageHash));
claimSignatures.push({ walletAddress: message.walletAddress, signature });
}
plan = await post('/verify', { ...request, claimSignatures });
}
const hashes: string[] = [];
for (const step of plan.steps) {
const tx = await wallet.sendTransaction(step.transaction);
await tx.wait();
hashes.push(tx.hash);
}
const confirmed = await post('/verify', { ...request, txHash: hashes[plan.confirmStepIndex] });
{
"success": true,
"data": {
"action": "SIGN_TRANSACTION",
"transaction": {
"to": "0xEf244c4Bd4d71Bd9A30EDF6Edef7CCC72642291a",
"data": "0x05bd97c8..."
},
"functionName": "verifyIdentity",
"description": "Verify 0xaBc123DEf456789012345678901234567890aBCD on the identity registry",
"registerKind": "TRUSSET",
"walletAddress": "0xaBc123DEf456789012345678901234567890aBCD",
"kycHash": "0xffb1f8c219725bd62205597cd7247c85cafeca20ac0a59f8ec6a7b979eaf672c",
"investorType": 2,
"softExpiry": 1822305600,
"hardExpiry": 1853841600,
"operation": "verify",
"confirmWith": {
"endpoint": "POST /customers/api/identity/verify",
"field": "txHash"
}
},
"error": null,
"metadata": {
"requestId": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-09-30T12:00:00.000Z"
}
}
{
"success": true,
"data": {
"register": "0x4C2b1a0f9E8d7c6B5a4938271605F4e3D2c1B0A9",
"registerKind": "TREX",
"signerAddress": "0x51A4F0c3B2e8d7A6c5B4e3f2A1d0c9b8e7f6A5D4",
"claimSigners": ["0x51A4F0c3B2e8d7A6c5B4e3f2A1d0c9b8e7f6A5D4"],
"action": "SIGN_MESSAGES",
"phase": "SIGN_CLAIMS",
"messages": [
{
"walletAddress": "0xaBc123DEf456789012345678901234567890aBCD",
"identity": "0x7d3e2c1b0A9F8E7D6C5b4A39281706f5E4d3C2b1",
"topic": "109015029463275868683647211611771110530627425002451180747115609001006883683411",
"claimIssuer": "0x9a8B7c6d5e4f30211203F4E5d6c7B8a998877665",
"data": "0xffb1f8c219725bd62205597cd7247c85cafeca20ac0a59f8ec6a7b979eaf672c",
"messageHash": "0x62db268a9c0c0485b5de02f7a6d1a266d17227af2a12a1c400581e6d726bc359",
"scheme": "eip191",
"description": "Sign keccak256(abi.encode(identity, topic, data)) as an EIP-191 personal message with a wallet that holds a CLAIM key on 0x9a8B7c6d5e4f30211203F4E5d6c7B8a998877665",
"claimType": "KYC"
}
],
"identities": {
"0xaBc123DEf456789012345678901234567890aBCD": "0x7d3e2c1b0A9F8E7D6C5b4A39281706f5E4d3C2b1"
},
"claimIssuer": "0x9a8B7c6d5e4f30211203F4E5d6c7B8a998877665",
"claimTopic": "109015029463275868683647211611771110530627425002451180747115609001006883683411",
"next": "Sign every messageHash as an EIP-191 personal message with a wallet that holds a CLAIM key on the trusted issuer, then repeat this call with claimSignatures: [{ walletAddress, signature }].",
"walletAddress": "0xaBc123DEf456789012345678901234567890aBCD",
"kycHash": "0xffb1f8c219725bd62205597cd7247c85cafeca20ac0a59f8ec6a7b979eaf672c",
"investorType": 2,
"softExpiry": 0,
"hardExpiry": 0,
"operation": "verify",
"confirmWith": {
"endpoint": "POST /customers/api/identity/verify",
"field": "txHash"
}
},
"error": null,
"metadata": {
"requestId": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-09-30T12:00:00.000Z"
}
}
{
"success": true,
"data": {
"register": "0x4C2b1a0f9E8d7c6B5a4938271605F4e3D2c1B0A9",
"registerKind": "TREX",
"signerAddress": "0x51A4F0c3B2e8d7A6c5B4e3f2A1d0c9b8e7f6A5D4",
"claimSigners": ["0x51A4F0c3B2e8d7A6c5B4e3f2A1d0c9b8e7f6A5D4"],
"action": "SIGN_TRANSACTIONS",
"phase": "REGISTER",
"steps": [
{
"action": "SIGN_TRANSACTION",
"stepType": "claim",
"walletAddress": "0xaBc123DEf456789012345678901234567890aBCD",
"identity": "0x7d3e2c1b0A9F8E7D6C5b4A39281706f5E4d3C2b1",
"transaction": {
"to": "0x7d3e2c1b0A9F8E7D6C5b4A39281706f5E4d3C2b1",
"data": "0xb1a34e0d..."
},
"functionName": "addClaim",
"description": "Add the KYC claim of 0xaBc123DEf456789012345678901234567890aBCD to its ONCHAINID 0x7d3e2c1b0A9F8E7D6C5b4A39281706f5E4d3C2b1",
"claimType": "KYC",
"dataHash": "0xffb1f8c219725bd62205597cd7247c85cafeca20ac0a59f8ec6a7b979eaf672c"
},
{
"action": "SIGN_TRANSACTION",
"stepType": "verify",
"walletAddress": "0xaBc123DEf456789012345678901234567890aBCD",
"transaction": {
"to": "0x4C2b1a0f9E8d7c6B5a4938271605F4e3D2c1B0A9",
"data": "0x454a03e0..."
},
"functionName": "registerIdentity",
"description": "Register 0xaBc123DEf456789012345678901234567890aBCD on the identity register 0x4C2b1a0f9E8d7c6B5a4938271605F4e3D2c1B0A9",
"ids": []
}
],
"confirmStepIndex": 1,
"identities": {
"0xaBc123DEf456789012345678901234567890aBCD": "0x7d3e2c1b0A9F8E7D6C5b4A39281706f5E4d3C2b1"
},
"claimIssuer": "0x9a8B7c6d5e4f30211203F4E5d6c7B8a998877665",
"claimTopic": "109015029463275868683647211611771110530627425002451180747115609001006883683411",
"registerStep": "registerIdentity",
"description": "Verify 0xaBc123DEf456789012345678901234567890aBCD on the identity registry",
"walletAddress": "0xaBc123DEf456789012345678901234567890aBCD",
"kycHash": "0xffb1f8c219725bd62205597cd7247c85cafeca20ac0a59f8ec6a7b979eaf672c",
"investorType": 2,
"softExpiry": 0,
"hardExpiry": 0,
"operation": "verify",
"confirmWith": {
"endpoint": "POST /customers/api/identity/verify",
"field": "txHash"
}
},
"error": null,
"metadata": {
"requestId": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-09-30T12:00:00.000Z"
}
}
{
"success": true,
"data": {
"walletAddress": "0xaBc123DEf456789012345678901234567890aBCD",
"status": "verified",
"operation": "verify",
"txHash": "0xdd729a9ad03451b7129c87e78e11b76bcd962d79d01e79ca69d95bb63342fdb9",
"gasUsed": "84521",
"kycHash": "0xffb1f8c219725bd62205597cd7247c85cafeca20ac0a59f8ec6a7b979eaf672c"
},
"error": null,
"metadata": {
"requestId": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-09-30T12:05:00.000Z"
}
}
{
"success": false,
"data": null,
"error": {
"code": "NOT_VERIFIED_ON_REGISTER",
"message": "The register holds no identity for this wallet yet. Register it, then confirm again."
},
"metadata": {
"requestId": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-09-30T12:05:00.000Z"
}
}
Error Codes
| Code | HTTP | Cause |
|---|---|---|
INVALID_ADDRESS | 400 | walletAddress is missing or not a valid address |
MISSING_COUNTRY | 400 | country is missing, not a string, or shorter than 2 characters |
INVALID_COUNTRY | 400 | country is not an ISO 3166-1 code |
INVALID_HASH | 400 | kycHash is not 32-byte hex |
INVALID_INVESTOR_TYPE | 400 | investorType names neither a preset nor one of your verification profiles, or is not a string. The message lists the accepted values. Also a disabled profile, on the build call, for a wallet whose customer is not already under it |
COUNTRY_BLOCKED | 403 | country is blocked on the verification profile named after investorType |
KYC_PROVIDER_ROLE_MISSING | 403 | Trusset register: none of your instance’s wallets holds KYC_PROVIDER_ROLE |
IDENTITY_HELD_BY_ANOTHER_ISSUER | 403 | Trusset ID Register: the wallet holds a valid identity another issuer registered, so this instance cannot update it |
REGISTRY_AGENT_MISSING | 403 | ERC-3643: none of your instance’s wallets is an agent of the register |
CLAIM_SIGNER_MISSING | 403 | ERC-3643: none of your instance’s wallets holds a claim key on the trusted issuer |
CLAIM_SIGNER_NOT_TRUSTED | 403 | ERC-3643: signerAddress, or the wallet a claim signature recovers to, holds no claim key on the trusted issuer |
INVALID_CLAIM_SIGNATURE | 400 | ERC-3643: a claim signature is not 65-byte hex, or recovers to no signer |
REGISTRY_NO_CLAIM_TOPICS | 409 | ERC-3643: the register requires no claim topic, so there is no claim to register |
REGISTRY_NO_TRUSTED_ISSUER | 409 | ERC-3643: the register names no trusted issuer for its claim topic |
ONCHAINID_AUTHORITY_UNKNOWN | 409 | ERC-3643: the wallet has no ONCHAINID, and no identity on the register reveals the implementation to deploy |
OWN_REGISTRY_NOT_CONFIGURED | 409 | The instance is bound to its own register but no valid register address is set |
REGISTRY_PROBE_FAILED | 503 | Your own register could not be read to tell its kind, or the trusted issuer could not be read to check a claim signer |
INVALID_TX | 400 | txHash is not 32-byte hex |
TX_NOT_FOUND | 404 | The transaction is not mined yet |
TX_REVERTED | 400 | The transaction reverted |
TX_WRONG_TARGET | 400 | The transaction does not write to the register, or it deployed a contract |
TX_WRONG_FUNCTION | 400 | The transaction calls a function this endpoint does not confirm |
TX_TARGET_MISMATCH | 400 | The transaction targets a different wallet, or an ONCHAINID that is not this wallet’s |
UNAUTHORIZED_SIGNER | 403 | Trusset ID Register: the transaction was not signed by one of your instance’s verified wallets |
TX_INVESTOR_TYPE_MISMATCH | 400 | Trusset register: the mined investor type differs from investorType in this request |
NOT_VERIFIED_ON_REGISTER | 409 | ERC-3643: the register does not report the wallet as verified yet |
CHAIN_UNAVAILABLE | 503 | ERC-3643: the transaction or the register could not be read |
VERIFY_FAILED | 400, 500 | Any other failure. 400 when a chain call or the transaction encoding failed |
⌘I
curl -X POST "https://api.trusset.org/customers/api/identity/verify" \
-H "Content-Type: application/json" \
-H "X-API-Key: trusset_your_key_here" \
-d '{
"walletAddress": "0xaBc123DEf456789012345678901234567890aBCD",
"country": "DE",
"investorType": "PROFESSIONAL",
"softExpiryDays": 365,
"hardExpiryDays": 730
}'
const url = 'https://api.trusset.org/customers/api/identity/verify';
const headers = {
'Content-Type': 'application/json',
'X-API-Key': 'trusset_your_key_here'
};
const request = {
walletAddress: '0xaBc123DEf456789012345678901234567890aBCD',
country: 'DE',
investorType: 'PROFESSIONAL',
softExpiryDays: 365,
hardExpiryDays: 730
};
const res = await fetch(url, { method: 'POST', headers, body: JSON.stringify(request) });
const { data } = await res.json();
const tx = await wallet.sendTransaction(data.transaction);
await tx.wait();
await fetch(url, {
method: 'POST',
headers,
body: JSON.stringify({ ...request, kycHash: data.kycHash, txHash: tx.hash })
});
import { ethers } from 'ethers';
const base = 'https://api.trusset.org/customers/api/identity';
const headers = {
'Content-Type': 'application/json',
'X-API-Key': 'trusset_your_key_here'
};
const post = async (path: string, body: Record<string, unknown>) => {
const res = await fetch(`${base}${path}`, { method: 'POST', headers, body: JSON.stringify(body) });
const json = await res.json();
if (!json.success) throw new Error(`${json.error.code}: ${json.error.message}`);
return json.data;
};
const request: Record<string, unknown> = {
walletAddress: '0xaBc123DEf456789012345678901234567890aBCD',
country: 'DE',
investorType: 'PROFESSIONAL',
signerAddress: await wallet.getAddress()
};
let plan = await post('/verify', request);
request.kycHash = plan.kycHash;
if (plan.phase === 'DEPLOY_IDENTITY') {
for (const step of plan.steps) {
const tx = await wallet.sendTransaction({ data: step.transaction.data });
await tx.wait();
await post('/onchainid/confirm', { walletAddress: step.walletAddress, txHash: tx.hash });
}
plan = await post('/verify', request);
}
if (plan.phase === 'SIGN_CLAIMS') {
const claimSignatures = [];
for (const message of plan.messages) {
const signature = await wallet.signMessage(ethers.utils.arrayify(message.messageHash));
claimSignatures.push({ walletAddress: message.walletAddress, signature });
}
plan = await post('/verify', { ...request, claimSignatures });
}
const hashes: string[] = [];
for (const step of plan.steps) {
const tx = await wallet.sendTransaction(step.transaction);
await tx.wait();
hashes.push(tx.hash);
}
const confirmed = await post('/verify', { ...request, txHash: hashes[plan.confirmStepIndex] });
{
"success": true,
"data": {
"action": "SIGN_TRANSACTION",
"transaction": {
"to": "0xEf244c4Bd4d71Bd9A30EDF6Edef7CCC72642291a",
"data": "0x05bd97c8..."
},
"functionName": "verifyIdentity",
"description": "Verify 0xaBc123DEf456789012345678901234567890aBCD on the identity registry",
"registerKind": "TRUSSET",
"walletAddress": "0xaBc123DEf456789012345678901234567890aBCD",
"kycHash": "0xffb1f8c219725bd62205597cd7247c85cafeca20ac0a59f8ec6a7b979eaf672c",
"investorType": 2,
"softExpiry": 1822305600,
"hardExpiry": 1853841600,
"operation": "verify",
"confirmWith": {
"endpoint": "POST /customers/api/identity/verify",
"field": "txHash"
}
},
"error": null,
"metadata": {
"requestId": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-09-30T12:00:00.000Z"
}
}
{
"success": true,
"data": {
"register": "0x4C2b1a0f9E8d7c6B5a4938271605F4e3D2c1B0A9",
"registerKind": "TREX",
"signerAddress": "0x51A4F0c3B2e8d7A6c5B4e3f2A1d0c9b8e7f6A5D4",
"claimSigners": ["0x51A4F0c3B2e8d7A6c5B4e3f2A1d0c9b8e7f6A5D4"],
"action": "SIGN_MESSAGES",
"phase": "SIGN_CLAIMS",
"messages": [
{
"walletAddress": "0xaBc123DEf456789012345678901234567890aBCD",
"identity": "0x7d3e2c1b0A9F8E7D6C5b4A39281706f5E4d3C2b1",
"topic": "109015029463275868683647211611771110530627425002451180747115609001006883683411",
"claimIssuer": "0x9a8B7c6d5e4f30211203F4E5d6c7B8a998877665",
"data": "0xffb1f8c219725bd62205597cd7247c85cafeca20ac0a59f8ec6a7b979eaf672c",
"messageHash": "0x62db268a9c0c0485b5de02f7a6d1a266d17227af2a12a1c400581e6d726bc359",
"scheme": "eip191",
"description": "Sign keccak256(abi.encode(identity, topic, data)) as an EIP-191 personal message with a wallet that holds a CLAIM key on 0x9a8B7c6d5e4f30211203F4E5d6c7B8a998877665",
"claimType": "KYC"
}
],
"identities": {
"0xaBc123DEf456789012345678901234567890aBCD": "0x7d3e2c1b0A9F8E7D6C5b4A39281706f5E4d3C2b1"
},
"claimIssuer": "0x9a8B7c6d5e4f30211203F4E5d6c7B8a998877665",
"claimTopic": "109015029463275868683647211611771110530627425002451180747115609001006883683411",
"next": "Sign every messageHash as an EIP-191 personal message with a wallet that holds a CLAIM key on the trusted issuer, then repeat this call with claimSignatures: [{ walletAddress, signature }].",
"walletAddress": "0xaBc123DEf456789012345678901234567890aBCD",
"kycHash": "0xffb1f8c219725bd62205597cd7247c85cafeca20ac0a59f8ec6a7b979eaf672c",
"investorType": 2,
"softExpiry": 0,
"hardExpiry": 0,
"operation": "verify",
"confirmWith": {
"endpoint": "POST /customers/api/identity/verify",
"field": "txHash"
}
},
"error": null,
"metadata": {
"requestId": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-09-30T12:00:00.000Z"
}
}
{
"success": true,
"data": {
"register": "0x4C2b1a0f9E8d7c6B5a4938271605F4e3D2c1B0A9",
"registerKind": "TREX",
"signerAddress": "0x51A4F0c3B2e8d7A6c5B4e3f2A1d0c9b8e7f6A5D4",
"claimSigners": ["0x51A4F0c3B2e8d7A6c5B4e3f2A1d0c9b8e7f6A5D4"],
"action": "SIGN_TRANSACTIONS",
"phase": "REGISTER",
"steps": [
{
"action": "SIGN_TRANSACTION",
"stepType": "claim",
"walletAddress": "0xaBc123DEf456789012345678901234567890aBCD",
"identity": "0x7d3e2c1b0A9F8E7D6C5b4A39281706f5E4d3C2b1",
"transaction": {
"to": "0x7d3e2c1b0A9F8E7D6C5b4A39281706f5E4d3C2b1",
"data": "0xb1a34e0d..."
},
"functionName": "addClaim",
"description": "Add the KYC claim of 0xaBc123DEf456789012345678901234567890aBCD to its ONCHAINID 0x7d3e2c1b0A9F8E7D6C5b4A39281706f5E4d3C2b1",
"claimType": "KYC",
"dataHash": "0xffb1f8c219725bd62205597cd7247c85cafeca20ac0a59f8ec6a7b979eaf672c"
},
{
"action": "SIGN_TRANSACTION",
"stepType": "verify",
"walletAddress": "0xaBc123DEf456789012345678901234567890aBCD",
"transaction": {
"to": "0x4C2b1a0f9E8d7c6B5a4938271605F4e3D2c1B0A9",
"data": "0x454a03e0..."
},
"functionName": "registerIdentity",
"description": "Register 0xaBc123DEf456789012345678901234567890aBCD on the identity register 0x4C2b1a0f9E8d7c6B5a4938271605F4e3D2c1B0A9",
"ids": []
}
],
"confirmStepIndex": 1,
"identities": {
"0xaBc123DEf456789012345678901234567890aBCD": "0x7d3e2c1b0A9F8E7D6C5b4A39281706f5E4d3C2b1"
},
"claimIssuer": "0x9a8B7c6d5e4f30211203F4E5d6c7B8a998877665",
"claimTopic": "109015029463275868683647211611771110530627425002451180747115609001006883683411",
"registerStep": "registerIdentity",
"description": "Verify 0xaBc123DEf456789012345678901234567890aBCD on the identity registry",
"walletAddress": "0xaBc123DEf456789012345678901234567890aBCD",
"kycHash": "0xffb1f8c219725bd62205597cd7247c85cafeca20ac0a59f8ec6a7b979eaf672c",
"investorType": 2,
"softExpiry": 0,
"hardExpiry": 0,
"operation": "verify",
"confirmWith": {
"endpoint": "POST /customers/api/identity/verify",
"field": "txHash"
}
},
"error": null,
"metadata": {
"requestId": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-09-30T12:00:00.000Z"
}
}
{
"success": true,
"data": {
"walletAddress": "0xaBc123DEf456789012345678901234567890aBCD",
"status": "verified",
"operation": "verify",
"txHash": "0xdd729a9ad03451b7129c87e78e11b76bcd962d79d01e79ca69d95bb63342fdb9",
"gasUsed": "84521",
"kycHash": "0xffb1f8c219725bd62205597cd7247c85cafeca20ac0a59f8ec6a7b979eaf672c"
},
"error": null,
"metadata": {
"requestId": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-09-30T12:05:00.000Z"
}
}
{
"success": false,
"data": null,
"error": {
"code": "NOT_VERIFIED_ON_REGISTER",
"message": "The register holds no identity for this wallet yet. Register it, then confirm again."
},
"metadata": {
"requestId": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-09-30T12:05:00.000Z"
}
}
