Skip to content
7 min read

Example Flows

Copy-paste curl walkthroughs for common use cases.

Replace placeholders:

  • $WALLIN_URL — your Wallin domain (e.g., https://app.wallin.example)
  • $TOKEN — Sanctum token from login
  • $OTP_KEY — OTP API key from /otp-applications

Flow 1: OTP Send & Verify

User requests a verification code, you send via WhatsApp, they verify.

#!/bin/bash

WALLIN_URL="https://app.wallin.example"
OTP_KEY="your_otp_key_here"
PHONE="+201001234567"

echo "=== Step 1: Send OTP ==="
RESPONSE=$(curl -s -X POST "$WALLIN_URL/api/v1/otp/send" \
  -H "X-Wallin-OTP-Key: $OTP_KEY" \
  -H "Content-Type: application/json" \
  -d "{\"phone_number\": \"$PHONE\"}")

echo "Response: $RESPONSE"

# Extract reference_id for later verification
REFERENCE=$(echo $RESPONSE | jq -r '.data.reference_id')
EXPIRES=$(echo $RESPONSE | jq -r '.data.expires_at')

echo "Reference ID: $REFERENCE"
echo "Expires at: $EXPIRES"

echo ""
echo "=== User receives OTP on WhatsApp ==="
echo "(Simulated code in test mode: usually 000000 or check logs)"
echo ""

echo "=== Step 2: User enters code (e.g., 123456) ==="
CODE="123456"

VERIFY=$(curl -s -X POST "$WALLIN_URL/api/v1/otp/verify" \
  -H "X-Wallin-OTP-Key: $OTP_KEY" \
  -H "Content-Type: application/json" \
  -d "{\"reference_id\": \"$REFERENCE\", \"code\": \"$CODE\"}")

echo "Verification response: $VERIFY"

VERIFIED=$(echo $VERIFY | jq -r '.data.verified')
if [ "$VERIFIED" = "true" ]; then
  echo "✓ OTP verified! User authenticated."
else
  echo "✗ OTP verification failed."
  REMAINING=$(echo $VERIFY | jq -r '.data.attempts_remaining')
  echo "Attempts remaining: $REMAINING"
fi

echo ""
echo "=== Step 3 (optional): Resend if user requests ==="
RESEND=$(curl -s -X POST "$WALLIN_URL/api/v1/otp/resend" \
  -H "X-Wallin-OTP-Key: $OTP_KEY" \
  -H "Content-Type: application/json" \
  -d "{\"reference_id\": \"$REFERENCE\"}")

NEW_EXPIRES=$(echo $RESEND | jq -r '.data.expires_at')
echo "New expiry: $NEW_EXPIRES"

Flow 2: Agent Bot (Automated Replies)

Log in as an agent, list conversations, send a reply, resolve.

#!/bin/bash

WALLIN_URL="https://app.wallin.example"
EMAIL="agent@example.com"
PASSWORD="agent_password"

echo "=== Step 1: Agent Login ==="
LOGIN=$(curl -s -X POST "$WALLIN_URL/api/agent/login" \
  -H "Content-Type: application/json" \
  -d "{\"email\": \"$EMAIL\", \"password\": \"$PASSWORD\", \"device_name\": \"bot\"}")

TOKEN=$(echo $LOGIN | jq -r '.token')
AGENT_ID=$(echo $LOGIN | jq -r '.user.id')

echo "Token: $TOKEN"
echo "Agent ID: $AGENT_ID"
echo ""

echo "=== Step 2: List Active Conversations ==="
CONVS=$(curl -s -X GET "$WALLIN_URL/api/agent/conversations?status=active&per_page=5" \
  -H "Authorization: Bearer $TOKEN")

echo "Conversations: $(echo $CONVS | jq '.data | length') found"
echo $CONVS | jq '.data[] | {id, customer_name, status, channel}'

# Pick the first conversation
CONV_ID=$(echo $CONVS | jq -r '.data[0].id')
echo ""
echo "Selected conversation ID: $CONV_ID"
echo ""

echo "=== Step 3: Send a Reply ==="
REPLY=$(curl -s -X POST "$WALLIN_URL/api/agent/conversations/$CONV_ID/messages/text" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"message": "Thank you for contacting us! We will get back to you shortly."}')

echo "Reply sent: $(echo $REPLY | jq -r '.success')"
echo ""

echo "=== Step 4: Add Label ==="
LABEL=$(curl -s -X POST "$WALLIN_URL/api/agent/conversations/$CONV_ID/labels" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"slug": "inquiry"}')

echo "Label added: $(echo $LABEL | jq -r '.success')"
echo ""

echo "=== Step 5: Resolve Conversation ==="
RESOLVE=$(curl -s -X POST "$WALLIN_URL/api/agent/conversations/$CONV_ID/resolve" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"notes": "Sent auto-response, marking resolved."}')

echo "Resolved: $(echo $RESOLVE | jq -r '.success')"

Flow 3: Send WhatsApp Template via API

Send a pre-approved template to a customer (requires Sanctum token).

#!/bin/bash

WALLIN_URL="https://app.wallin.example"
TOKEN="your_sanctum_token"
WABA_ID=1  # WhatsApp Business Account ID
RECIPIENT="+201001234567"
TEMPLATE_NAME="hello_world"
TEMPLATE_LANG="en"

echo "=== Send WhatsApp Template ==="

SEND=$(curl -s -X POST "$WALLIN_URL/api/whatsapp/send-template" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d "{
    \"business_account_id\": $WABA_ID,
    \"to\": \"$RECIPIENT\",
    \"template_name\": \"$TEMPLATE_NAME\",
    \"language_code\": \"$TEMPLATE_LANG\",
    \"parameters\": [\"Ahmad\", \"2024-07-19\"]
  }")

echo "Response:"
echo $SEND | jq .

SUCCESS=$(echo $SEND | jq -r '.success')
if [ "$SUCCESS" = "true" ]; then
  MSG_ID=$(echo $SEND | jq -r '.message_id')
  WA_ID=$(echo $SEND | jq -r '.whatsapp_message_id')
  echo "✓ Message sent!"
  echo "  Local ID: $MSG_ID"
  echo "  WhatsApp ID: $WA_ID"
else
  echo "✗ Send failed"
  echo $SEND | jq '.error'
fi

Flow 4: Custom Integration (Event Webhook)

Set up a webhook to trigger custom logic when messages arrive.

Example: Auto-assign to supervisor if label contains "urgent"

# Flask endpoint (your server)
from flask import Flask, request
import hmac
import hashlib
import json

app = Flask(__name__)

WALLIN_WEBHOOK_SECRET = "your_webhook_secret"

@app.route('/webhook/wallin', methods=['POST'])
def wallin_webhook():
    # Verify signature
    payload = request.get_data()
    signature = request.headers.get('X-Wallin-Signature')
    
    expected_sig = hmac.new(
        WALLIN_WEBHOOK_SECRET.encode(),
        payload,
        hashlib.sha256
    ).hexdigest()
    
    if not hmac.compare_digest(signature, expected_sig):
        return {'error': 'Invalid signature'}, 403
    
    event = request.json
    
    # Handle message.received event
    if event.get('type') == 'message.received':
        message = event['data']['message']
        conversation_id = event['data']['conversation_id']
        
        # Check if message contains "urgent"
        if 'urgent' in message.get('content', {}).get('text', '').lower():
            # Call Wallin API to assign to supervisor
            supervisor_id = 42
            
            requests.post(
                f'https://wallin.example/api/agent/conversations/{conversation_id}/assign',
                headers={'Authorization': f'Bearer {api_token}'},
                json={'agent_id': supervisor_id}
            )
    
    return {'status': 'ok'}, 200

if __name__ == '__main__':
    app.run(port=5000)

Configure webhook at /channels/{connectionId} → Webhooks → URL: https://your-domain.com/webhook/wallin

Flow 5: Bulk Agent Assignment

Assign multiple conversations to an agent at once.

#!/bin/bash

WALLIN_URL="https://app.wallin.example"
TOKEN="your_sanctum_token"
AGENT_ID=5  # Target agent

echo "=== List Pending Conversations ==="
CONVS=$(curl -s -X GET "$WALLIN_URL/api/agent/conversations?status=pending&per_page=100" \
  -H "Authorization: Bearer $TOKEN")

# Extract IDs
CONV_IDS=($(echo $CONVS | jq -r '.data[].id'))

echo "Found ${#CONV_IDS[@]} pending conversations"
echo ""

echo "=== Bulk Assign to Agent $AGENT_ID ==="
for CONV_ID in "${CONV_IDS[@]}"; do
  echo -n "Assigning conversation $CONV_ID... "
  
  RESULT=$(curl -s -X POST "$WALLIN_URL/api/agent/conversations/$CONV_ID/assign" \
    -H "Authorization: Bearer $TOKEN" \
    -H "Content-Type: application/json" \
    -d "{\"agent_id\": $AGENT_ID}")
  
  if [ "$(echo $RESULT | jq -r '.success')" = "true" ]; then
    echo "✓"
  else
    echo "✗"
  fi
  
  # Rate limit: small delay between requests
  sleep 0.1
done

echo "Done!"

Flow 6: Monitor Conversation Status

Poll conversation status periodically (fallback if WebSocket unavailable).

#!/bin/bash

WALLIN_URL="https://app.wallin.example"
TOKEN="your_sanctum_token"
CONV_ID=42
POLL_INTERVAL=5  # seconds

echo "=== Poll Conversation for Updates ==="

while true; do
  CONV=$(curl -s -X GET "$WALLIN_URL/api/agent/conversations/$CONV_ID" \
    -H "Authorization: Bearer $TOKEN")
  
  STATUS=$(echo $CONV | jq -r '.data.status')
  UNREAD=$(echo $CONV | jq -r '.data.unread_count')
  LAST_MSG=$(echo $CONV | jq -r '.data.last_message_at')
  
  echo "[$(date '+%H:%M:%S')] Status: $STATUS | Unread: $UNREAD | Last: $LAST_MSG"
  
  # Fetch new messages
  MESSAGES=$(curl -s -X GET "$WALLIN_URL/api/agent/conversations/$CONV_ID/messages?per_page=5" \
    -H "Authorization: Bearer $TOKEN")
  
  NEW_MSG_COUNT=$(echo $MESSAGES | jq '.data | length')
  if [ $NEW_MSG_COUNT -gt 0 ]; then
    echo "  New messages: $NEW_MSG_COUNT"
    echo $MESSAGES | jq '.data[0] | {content, created_at}'
  fi
  
  # Exit if resolved
  if [ "$STATUS" = "resolved" ]; then
    echo "Conversation resolved. Exiting."
    break
  fi
  
  sleep $POLL_INTERVAL
done

Troubleshooting Examples

"OTP rate limit exceeded"

# Wait 1 hour
sleep 3600

# Or try a different phone number
PHONE_2="+201009876543"
curl -X POST "$WALLIN_URL/api/v1/otp/send" \
  -H "X-Wallin-OTP-Key: $OTP_KEY" \
  -H "Content-Type: application/json" \
  -d "{\"phone_number\": \"$PHONE_2\"}"

"WhatsApp message window closed"

# Use a template instead (if template supports it)
curl -X POST "$WALLIN_URL/api/whatsapp/send-template" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d "{
    \"business_account_id\": 1,
    \"to\": \"$RECIPIENT\",
    \"template_name\": \"marketing_template\",
    \"language_code\": \"en\"
  }"

"Invalid API key" (OTP)

# Verify key at /otp-applications
# Regenerate if needed, then retry:
OTP_KEY="newly_generated_key"

curl -X POST "$WALLIN_URL/api/v1/otp/send" \
  -H "X-Wallin-OTP-Key: $OTP_KEY" \
  -H "Content-Type: application/json" \
  -d "{\"phone_number\": \"$PHONE\"}"
Was this page helpful?

See Wallin on your own channels.

Book a walkthrough — we'll connect a test account and show you the inbox, broadcasts, and automation live.