Authentication
OTP-based sign-in over phone or email. No passwords. The SDK manages the session token end-to-end; a successful `verifyOtp` call wires the token into every subsequent request.
Construct a client
import { createCimplifyClient } from '@cimplify/sdk'
export const client = createCimplifyClient({
publicKey: process.env.NEXT_PUBLIC_CIMPLIFY_PUBLIC_KEY!,
})Full sign-in flow
// 1. Send the code (single string contact: phone in E.164 or email)
const sent = await client.auth.requestOtp('+233244000000')
if (!sent.ok) {
console.error(sent.error.code, sent.error.message)
return
}
// 2. Verify: pass the user-entered code first, then the same contact
const verified = await client.auth.verifyOtp('123456', '+233244000000')
if (!verified.ok) {
console.error(verified.error.code, verified.error.message)
return
}
// 3. The SDK has already wired the session token into the client.
// All subsequent requests are authenticated automatically.
console.log(verified.value.customer.id)requestOtp
Single string argument. The server infers phone vs email from the value. Pass { idempotencyKey } to dedupe a retried send.
// Phone (E.164)
await client.auth.requestOtp('+233244000000')
// Email
await client.auth.requestOtp('jane@example.com')
// Custom idempotency key (otherwise the SDK auto-generates one)
await client.auth.requestOtp('+233244000000', undefined, {
idempotencyKey: 'send-otp-2024-01-15-abc',
})verifyOtp
Signature is verifyOtp(code, contact?): the code first, then the contact you sent it to. On success the SDK calls setAccessToken() internally; you do not need to.
const result = await client.auth.verifyOtp('123456', '+233244000000')
if (!result.ok) {
// Codes you'll see: VALIDATION_ERROR, OTP_EXPIRED, OTP_INVALID
console.error(result.error.code, result.error.message)
return
}
const { customer, session_token } = result.value
// session_token is already attached to the client. It's exposed here only
// for cases where you also need to persist it server-side.Session helpers
const status = await client.auth.getStatus()
if (status.ok) {
console.log(status.value.is_authenticated)
console.log(status.value.customer)
console.log(status.value.session_expires_at)
}
const user = await client.auth.getCurrentUser() // Result<Customer | null>
const authed = await client.auth.isAuthenticated() // Result<boolean>updateProfile
Patch the authenticated customer. All fields optional; send only what changes.
const updated = await client.auth.updateProfile({
name: 'Jane Doe',
email: 'jane@example.com',
phone: '+233244000000',
})
if (updated.ok) {
console.log(updated.value.id, updated.value.name)
}logout
Clears the server session and the client-side token in one call.
await client.auth.logout()
// client.getAccessToken() is now undefined; subsequent requests are anonymousReact: a sign-in form
'use client'
import { useState } from 'react'
import { useCimplifyClient } from '@cimplify/sdk/react'
export function SignInForm() {
const client = useCimplifyClient()
const [contact, setContact] = useState('')
const [code, setCode] = useState('')
const [stage, setStage] = useState<'request' | 'verify'>('request')
const [error, setError] = useState<string | null>(null)
async function send() {
const r = await client.auth.requestOtp(contact)
if (!r.ok) return setError(r.error.message)
setStage('verify')
}
async function verify() {
const r = await client.auth.verifyOtp(code, contact)
if (!r.ok) return setError(r.error.message)
setError(null)
}
return stage === 'request' ? (
<form onSubmit={(e) => { e.preventDefault(); send() }}>
<input value={contact} onChange={(e) => setContact(e.target.value)} placeholder="Phone or email" />
<button type="submit">Send code</button>
{error && <p>{error}</p>}
</form>
) : (
<form onSubmit={(e) => { e.preventDefault(); verify() }}>
<input value={code} onChange={(e) => setCode(e.target.value)} placeholder="6-digit code" />
<button type="submit">Verify</button>
{error && <p>{error}</p>}
</form>
)
}Method reference
| Method | Returns |
|---|---|
requestOtp(contact, contactType?, opts?) | Result<void> |
verifyOtp(code, contact?, opts?) | Result<OtpResult> |
getStatus() | Result<AuthStatus> |
getCurrentUser() | Result<Customer | null> |
isAuthenticated() | Result<boolean> |
updateProfile(input) | Result<Customer> |
logout() | Result<{ success: boolean }> |
Related
-
Checkout Place an order against the authenticated session
-
Orders List and inspect the signed-in customer's orders
-
Error handling CimplifyError shape, OTP_EXPIRED / OTP_INVALID
-
Idempotency Replay-safe OTP requests
Checkout
Convert a cart into a paid order. The body is **flat**: fields like ` cart_id`, `customer`, `order_type`, and `payment_method` sit at the top level (not nested under any envelope). Production uses ` #[serde(flatten)]`; the SDK matches that shape exactly.
Orders
List, retrieve, and cancel orders. Anonymous orders carry a short-lived `bill_token` that the SDK persists for you, so guest order lookups Just Work after checkout.