curl -X POST https://grail-stack-dev.onrender.com/v1/users \
-H "x-api-key: grail_partner_<hex>" \
-H "Content-Type: application/json" \
-d '{
"user_id": "partner_internal_user_001",
"wallet_address": "2u7vVGJCTtsijCqWJNCEknVkuLjeU7Rd4PMg4dXuTZGx",
"kyc": {
"country": "PK",
"full_name": "Alice Example",
"kyc_provider": "manual",
"kyc_level": "full",
"kyc_verified_at": "2026-04-17T00:00:00Z",
"kyc_data": {
"id_type": "CNIC",
"id_number": "12345-1234567-1"
}
}
}'
{
"grail_user_id": "gu_6b60956e-a8ee-4de2-8128-04c7fdf633c3",
"user_id": "partner_internal_user_001",
"wallet_address": "2u7vVGJCTtsijCqWJNCEknVkuLjeU7Rd4PMg4dXuTZGx",
"created_at": "2026-04-17T10:15:30.000Z"
}
Users
Create User
Registers an end-user under the authenticated partner. KYC is flexible JSON — core fields are required, provider-specific data lives in kyc_data.
POST
/
v1
/
users
curl -X POST https://grail-stack-dev.onrender.com/v1/users \
-H "x-api-key: grail_partner_<hex>" \
-H "Content-Type: application/json" \
-d '{
"user_id": "partner_internal_user_001",
"wallet_address": "2u7vVGJCTtsijCqWJNCEknVkuLjeU7Rd4PMg4dXuTZGx",
"kyc": {
"country": "PK",
"full_name": "Alice Example",
"kyc_provider": "manual",
"kyc_level": "full",
"kyc_verified_at": "2026-04-17T00:00:00Z",
"kyc_data": {
"id_type": "CNIC",
"id_number": "12345-1234567-1"
}
}
}'
{
"grail_user_id": "gu_6b60956e-a8ee-4de2-8128-04c7fdf633c3",
"user_id": "partner_internal_user_001",
"wallet_address": "2u7vVGJCTtsijCqWJNCEknVkuLjeU7Rd4PMg4dXuTZGx",
"created_at": "2026-04-17T10:15:30.000Z"
}
Overview
Registers an end-user under the authenticated partner and stores their KYC record. The user must havekyc_level: "full" before they can quote trades or redemptions.
Core KYC fields (country, full_name, kyc_provider, kyc_level, kyc_verified_at) are structured columns; provider-specific data goes in the free-form kyc_data object. If kyc_data includes id_type and id_number, GRAIL validates the id_number against the regex registered for that country/ID-type pair (advisory — absent regex means no validation is enforced).
Headers
string
required
A valid
PARTNER scope key.Request Body
string
required
Your internal identifier for this user. Unique within your partner (two users cannot share the same
user_id). Stored as partner_user_id in GRAIL.string
required
The user’s Solana wallet address. Must be a valid pubkey, and globally unique across all GRAIL users.
object
required
The user’s KYC record.
Show properties
Show properties
string
required
ISO-3166-1 alpha-2 country code. Must match an active country in GRAIL’s country list (e.g.,
PK, AE, SA).string
required
Full legal name. 1–255 characters.
string
required
Label identifying the KYC provider you used (e.g.,
"sumsub", "manual"). Free-form string — stored verbatim.string
required
Must be
"full". Partial or basic KYC is rejected.string
required
ISO-8601 timestamp of when KYC was completed on your side.
object
Optional free-form JSON for provider-specific fields. If
id_type and id_number are included, the server performs advisory validation of id_number against the country’s registered regex.Response
string
GRAIL’s identifier for the user, prefixed
gu_. Use this in all subsequent trade and redemption calls.string
Echo of your
user_id (the partner_user_id).string
Echo of
wallet_address.string
ISO-8601 timestamp of user creation.
Errors
| HTTP | error | When |
|---|---|---|
| 400 | invalid_request | Missing required fields, bad timestamp, bad full_name, kyc_data.id_number doesn’t match the regex |
| 400 | invalid_wallet | wallet_address is not a valid Solana pubkey |
| 400 | kyc_level_insufficient | kyc.kyc_level is not "full" |
| 409 | user_already_exists | wallet_address is already registered, or user_id is already registered for this partner |
curl -X POST https://grail-stack-dev.onrender.com/v1/users \
-H "x-api-key: grail_partner_<hex>" \
-H "Content-Type: application/json" \
-d '{
"user_id": "partner_internal_user_001",
"wallet_address": "2u7vVGJCTtsijCqWJNCEknVkuLjeU7Rd4PMg4dXuTZGx",
"kyc": {
"country": "PK",
"full_name": "Alice Example",
"kyc_provider": "manual",
"kyc_level": "full",
"kyc_verified_at": "2026-04-17T00:00:00Z",
"kyc_data": {
"id_type": "CNIC",
"id_number": "12345-1234567-1"
}
}
}'
{
"grail_user_id": "gu_6b60956e-a8ee-4de2-8128-04c7fdf633c3",
"user_id": "partner_internal_user_001",
"wallet_address": "2u7vVGJCTtsijCqWJNCEknVkuLjeU7Rd4PMg4dXuTZGx",
"created_at": "2026-04-17T10:15:30.000Z"
}
