Getting Started · Step 2 of 5

Users & IBANs

Every participant in a contract must exist in Waffy and be linked to your client. One endpoint handles all cases — you never need to check state first.

  1. 1Auth
  2. 2Users
  3. 3Contract
  4. 4Payment
  5. 5Settle

Step 3 — Sign up / link user

Call this for every user (buyer and seller) before creating a contract. One call handles all three cases automatically:

  • User not registered → registers them → links them → returns customer token
  • User registered but not linked → links them → returns customer token
  • User registered and already linked → returns a fresh customer token
bash
curl -s "$WAFFY_AUTH_URL/v2/api/users/sign-up" \
  -H "Authorization: Bearer $APP_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "phoneNumber": "+966XXXXXXXXX",
    "firstName": "Ahmed",
    "lastName": "Al-Ghamdi",
    "clientUserId": "your_internal_user_id"
  }'

Response:

json
{
  "data": {
    "id": 99001,
    "phoneNumber": "+966XXXXXXXXX",
    "preExistingUser": false,
    "clientUserToken": "eyJhbGc..."
  }
}

Save data.id as seller_id or buyer_id. Call it twice — once for each party.

Store clientUserToken securely

This is the user's Waffy password — not a temporary token despite the name. Store it encrypted at rest, never log it, never expose it to the browser. You'll use it as the password in an OAuth password-grant to fetch a fresh customer_token at payment time. Do not send password in the sign-up body — Waffy generates and returns it.

preExistingUser: true

Means the user already existed in Waffy. The endpoint linked them to your client automatically — nothing extra needed. You still get a fresh clientUserToken in the response.

Step 4 — Add an IBAN

Required for anyone who will receive money — the seller, and the buyer in case of a refund. Also required alongside a valid address for bank transfer cash-out. Saudi format: SA + 22 digits.

bash
curl -s "$WAFFY_AUTH_URL/api/users/$SELLER_ID/IBAN" \
  -H "Authorization: Bearer $USER_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "iban": "SA0380000000608010167519",
    "currency": "SAR",
    "beneficiaryName": "Ahmed Al-Ghamdi",
    "nationalId": "1000000000",
    "accountType": "PERSONAL"
  }'

# Repeat for buyer with their own IBAN + nationalId

Step 5 — Add an address

Required for bank transfer cash-out alongside IBAN. Use user_token (org admin token).

bash
curl -s "$WAFFY_AUTH_URL/api/profiles/$SELLER_ID/addresses" \
  -H "Authorization: Bearer $USER_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "addressLabel": "HOME",
    "street": "King Fahd Road",
    "district": "Al Olaya",
    "city": "Riyadh",
    "countryCode": "SA",
    "postalCode": "12271"
  }'

# Repeat for buyer

Absher identity verification (optional — KYC)

Two-step flow only needed if your org requires Saudi national ID verification. The user enters their national ID — Absher validates it and sends an OTP to the phone registered with that ID in the government system (may differ from their Waffy phone).

Step 1 — Submit national ID

Waffy forwards the ID to Absher. If valid, Absher sends an OTP to the user's Absher-registered phone. nationalId must be exactly 10 digits.

bash
curl -s -X PATCH "$WAFFY_AUTH_URL/api/users/external/$USER_ID/national-id" \
  -H "Authorization: Bearer $USER_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "nationalId": "1000000000"
  }'

Step 2 — Validate the OTP

bash
curl -s "$WAFFY_AUTH_URL/api/external/otps/validate" \
  -H "Authorization: Bearer $USER_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "type": "ABSHER_OTP",
    "phoneNumber": "+966XXXXXXXXX",
    "otp": 123456
  }'

Response:

json
{
  "data": {
    "valid": true
  }
}
OTP typeUse
WAFFY_OTPStandard Waffy OTP verification
ABSHER_OTPSaudi national ID verification via Nafath/Absher