Customer identity verification & OTP

Before your bot discusses anything personal it proves the customer is the account holder: a name plus one more detail, then a one-time code sent to contact details you already hold. This guide specifies every rule, message and limit in that process.
14 min readhttps://www.siluxchat.comcurl · Python · PHP · Node.js

Overview

Verification is two calls. verify-identity matches the details the customer gives and sends a six-digit code; verify-otp checks the code and returns a session_token. Every account endpoint then requires that session. The same contract applies whether the endpoints are yours (Account Connect) or provided by Silux Chat on top of a connector.

Account Connect (your endpoints)Connector (our endpoints on your vendor)
Who matches the customerYour systemSilux Chat, by looking the customer up in your vendor system (or, where the vendor has no customer search, on their own orders or bookings)
Who sends the codeYour system, to the registered mobileSilux Chat, to the mobile or email the vendor holds
Code and session lifetimesYou decide (recommended 10 and 30 minutes)10 minutes and 30 minutes
Wrong-code limitYou enforce (recommended 5)5, enforced by the platform
Base addressYours, e.g. https://api.yourcompany.com/siluxhttps://www.siluxchat.com/api/connect/{connection_id}

The full sequence

Verification state machine
 ┌──────────────┐  name + postcode/email/mobile   ┌────────────────────┐
 │  UNVERIFIED  │ ───────────────────────────────►│  verify-identity   │
 └──────────────┘                                 └─────────┬──────────┘
        ▲   success:false (no detail on WHY)                │ success:true
        │◄──────────────────────────────────────────────────┤ otp_token (10 min)
        │                                                   ▼
        │                                         ┌────────────────────┐
        │   5 wrong codes / token expired         │   CODE SENT        │
        │◄─────────────────────────────────────── │   verify-otp       │◄─┐ wrong code
        │                                         └─────────┬──────────┘  │ (tries < 5)
        │                                                   │ correct ────┘
        │                                                   ▼
        │                                         ┌────────────────────┐
        │   session expires (30 min) → 401        │     VERIFIED       │
        └──────────────────────────────────────── │  session_token     │
                                                  └────────────────────┘
                                   account-summary · orders · invoices · bookings · subscriptions · tickets
  1. 1
    The customer asks something personal. The bot asks for their full name and one more detail.
  2. 2
    The bot sends verify-identity. On a match, a code goes to contact details already on the account and an otp_token comes back.
  3. 3
    The bot tells the customer where the code went (“a text to the mobile ending 123”) and asks for it.
  4. 4
    The bot sends verify-otp with the token and the code. On success it receives a session_token.
  5. 5
    Account tools are unlocked for that conversation until the session expires.

Step 1: verify-identity

POST{base}/verify-identityX-API-Key

Matches the customer and sends a one-time code.

FieldTypeNotes
full_namestringAlways sent. Compare case- and space-insensitively.
postcodestringMay be empty. Compare without spaces: SW1A1AA equals SW1A 1AA.
emailstringMay be empty.
mobile_numberstringMay be empty. Accept 07… and +44 7… forms.
Against the live demo
curl -s -X POST "https://www.siluxchat.com/api/account-connect-demo/v1/verify-identity" \
  -H "X-API-Key: demo-account-connect-key" \
  -H "Content-Type: application/json" \
  -d '{"full_name":"Test Customer","postcode":"TE5 7ST"}'
Matched (200)
{
  "success": true,
  "otp_token": "eyJrIjoib3RwIiwiYyI6IkMxMDAxIiwiZXhwIjoxNzkwNDE2NTg2LjI4NDIyNDN9.443c18a766f3c03f371e3da90a269fdf",
  "message": "Code sent to mobile ending 000"
}
Not matched (200)
{
  "success": false,
  "message": "We could not match those details to an account."
}

A “no” is HTTP 200 with "success": false. Reserve 401 for a missing or wrong API key. The message is shown to the customer in substance, so keep it short and neutral.

Never say which detail was wrong

“That postcode doesn’t match” tells an attacker the name was right. Always return the same message for every kind of mismatch, and for accounts that do not exist.

Matching rules

Account Connect

Your system decides, but the specification is: the full name must match, and at least one of postcode, email or mobile must match the same account. Normalise before comparing — lower-case, remove spaces.

Connectors

  • The customer is looked up in your vendor system by email, and otherwise by mobile. A postcode alone cannot find an account, so on a connector the bot asks for an email address or mobile number.
  • The surname (last word of the name) must match the record exactly, and the first name must either match or start with the same letter — so “Jan Smith” or “Janey Smith” match Jane Smith. Single-letter words are ignored, so a bare initial (“J Smith”) is not enough.
  • Then one more detail must agree with the same record: postcode, email or mobile.
  • Mobile numbers are compared on their last 10 digits, so 07700900123 and +447700900123 are the same number.
  • If the vendor cannot be reached or refuses the credentials, the reply is {"success": false, "system_error": true, "message": "We can't check accounts right now — our team has been told."}. The bot never tells a customer “no account found” when the truth is that it could not look.
Customer saysRecord holdsResult
Jane Smith, jane@example.comJane Smith, jane@example.comMatch
Janey Smith, +44 7700 900123Jane Smith, 07700900123Match (same first letter + same mobile)
J Smith, jane@example.comJane Smith, jane@example.comNo match (a bare initial is ignored)
Jane Smyth, jane@example.comJane Smith, jane@example.comNo match (surname)
Jane Smith, SW1A 1AA onlyJane Smith, SW1A 1AAConnector: cannot look up without email or mobile. Account Connect: match.

Connectors with no customer search

Some vendors (Cal.com, Google Calendar, Etsy, eBay, AfterShip, ShipStation, Printful, Workiz) have no way to search customers, but their bookings or orders name the customer. For these, the customer’s own records are the proof:

  • The bot needs the full name and the email address the customer used; a mobile number is not accepted here. Without a valid email the reply is {"success": false, "needs": ["full_name", "email"], …}.
  • Their orders or bookings are looked up by that email. A record must name exactly that email and a name that passes the same surname-and-first-name rule above.
  • The code is then emailed to that address — never texted, and never sent to a number typed in the chat.
  • Once verified, only records that name the customer’s email are shown; a record that does not say whose it is, is not shown.
  • A wrong name and a wrong email get the same reply, so the answer never reveals which detail was wrong.

Connectors that hold neither (carriers, Trello, Asana, Twilio and similar) cannot verify customers. verify-identity on them replies {"success": false, "not_supported": true, "message": "<Name> can't be used to check who you are — …"}. Carriers still answer parcel tracking by tracking number without verification — see Orders, delivery tracking & returns.

Where the code is sent

The code always goes to contact details already held on the account. The chat never chooses the destination, so knowing someone’s name and postcode is not enough to read their code.

RouteDestinationCustomer-facing message
Account ConnectYour system texts the mobile registered on the account.Code sent to mobile ending 123
Connector, business has SMS enabledA text to the mobile the vendor holds.Code sent by a text to the mobile ending 123
Connector, no SMS or no mobileAn email to the address the vendor holds.Code sent by an email to j***@example.com
Connector, no mobile or email on fileNothing is sent.We found the account but have no mobile or email on it to send a code to — please contact us directly.
Connector verifying from orders/bookingsAn email to the address on those records (the one the customer gave).Code sent by an email to j***@example.com
The text a connector sends
<Your business name> verification code: 482913. It expires in 10 minutes. Never share it.

Mask what you reveal

Only ever reveal the last three digits of a mobile or the first letter of an email address. That is enough for a genuine customer to recognise, and useless to anyone else.

Step 2: verify-otp

POST{base}/verify-otpX-API-Key

Body: {"otp_token": "…", "otp_code": "482913"}. Strip spaces from the code before comparing.

Against the live demo
# export OTP=<otp_token from verify-identity>
: "${OTP:?export OTP first}"
curl -s -X POST "https://www.siluxchat.com/api/account-connect-demo/v1/verify-otp" \
  -H "X-API-Key: demo-account-connect-key" \
  -H "Content-Type: application/json" \
  -d "{\"otp_token\":\"$OTP\",\"otp_code\":\"123456\"}"
Correct code (200)
{
  "success": true,
  "session_token": "eyJrIjoic2Vzc2lvbiIsImMiOiJDMTAwMiIsImV4cCI6MTc5MDQxODAxMC45MTg4OTY3fQ.9c2f16ac077d2df711c89a5bc412537c"
}
SituationStatusmessage
Wrong code200That code is not correct.
Token unknown or older than 10 minutes200That code has expired — please ask for a new one.
Fifth wrong code (connectors; recommended for yours)200Too many wrong codes — please ask for a new one.
Missing or wrong API key401Missing or wrong X-API-Key

Time and attempt limits

LimitValueWhat happens at the limit
Code lifetime10 minutesverify-otp replies “That code has expired”; the bot offers to send a new code.
Wrong codes per otp_token5The token is destroyed; the customer must start again with a fresh code.
Session lifetime30 minutesAccount endpoints return 401 “Session expired — please verify again.” and the bot re-verifies.
Codes sent per conversationCapped by the platformFurther requests are routed to your team instead of sending more texts.

A used code is destroyed on success, so the same code can never open a second session. Store only a hash of each code on your side, as the reference implementations do.

Using the session

Send the session_token in the JSON body of every account call. It identifies the verified customer; the bot never sends a customer ID of its own choosing, so it cannot ask for anyone else’s data.

Read the verified customer's account
# export SESSION=<session_token from verify-otp>
: "${SESSION:?export SESSION first}"
curl -s -X POST "https://www.siluxchat.com/api/account-connect-demo/v1/account-summary" \
  -H "X-API-Key: demo-account-connect-key" \
  -H "Content-Type: application/json" \
  -d "{\"session_token\":\"$SESSION\"}"
Expired or unknown session (401)
{"detail": {"success": false, "message": "Session expired — please verify again."}}

Continue with Account lookups.

The test customer

So the dashboard can test the chain without texting anyone, every implementation recognises one fictional customer:

full_namepostcodeemailmobilecode
Test CustomerTE5 7STtest@example.com07700900000123456 — never sent
  • Account Connect: your code must accept the test customer, issue an otp_token without sending anything, accept 123456 and return a little sample data.
  • Connectors: Silux Chat intercepts the test customer, proves the credentials with a real read-only call to your vendor using a made-up customer, and replies "Test customer — no code is sent; use 123456". Account calls in a test session read no customer data and return "test": true.

Why this is safe

  • Possession, not knowledge. Names, postcodes and emails leak; the code proves the person controls the phone or inbox on the account.
  • Enforced in code. The bot’s account tools will not run without a session, whatever it is told in the chat.
  • No enumeration. Identical messages for every mismatch, plus attempt limits, stop guessing.
  • Short-lived. Ten-minute codes and thirty-minute sessions limit the value of anything intercepted.
  • Honest failures. An unreachable system produces “can’t check right now”, never a false “no account”.

More in Security & data protection.

Get help

If genuine customers are being rejected, or codes are not arriving, email support@siluxcall.co.uk with the time of the attempt and your connection name — never the customer’s code.

Need help with your integration?

Our integration team can review your endpoints, help you choose the right connector and check a test run with you. Tell us which system you use and what you would like the bot to answer.

support@siluxcall.co.uk