WABridges

Python quickstart

Key, bridge, phone. Then a message.

Run these in order. Your first bridge is free for 7 days with a card on file, nothing charged until the trial ends.

1
Get a key, install the client
One key for the whole account

Create an API key at Dashboard → API keys. It starts with sk_ and is shown once, so copy it now. Every call below sends it as Authorization: Bearer $WA_API_KEY.

No phone handy? Every account has a simulated bridge at customer_ref = sandbox. Skip steps 2 and 3, use sandbox wherever a customer_ref appears below, and send to 15550001234, a fake contact who replies within seconds. Events show on your dashboard.
bash
pip install requests flask
bash
export WA_API_KEY="sk_..."
2
Provision a bridge
One bridge is one WhatsApp number

Your first bridge is created from the dashboard: one click connects a number and starts the 7-day free trial (card on file, $0 today). Every API call below can address it as /api/instances/default/…: default always means your first bridge, so no name goes in the URL. The call here is how you add more bridges to the subscription.

customer_ref is your internal identifier (user ID, slug, anything you control). webhook_url is optional: leave it out to use the hosted inbox and read events on the dashboard, change it any time with PATCH.

Store the webhook_secret that comes back. It signs every webhook the bridge sends you.

Billing. Every bridge is a $5/month seat on your subscription. Bridges added during the trial are free until it ends. Without a subscription this call returns 402 no_subscription: connect a number in the dashboard first.
python
import os
import requests

API_KEY = os.environ['WA_API_KEY']
BASE    = 'https://wabridges.com/api'
HEADERS = {'Authorization': f'Bearer {API_KEY}'}

def provision(customer_ref, webhook_url):
    r = requests.post(f'{BASE}/instances', json={
        'customer_ref': customer_ref,
        'webhook_url':  webhook_url,
    }, headers=HEADERS)
    r.raise_for_status()
    return r.json()  # { 'id', 'customer_ref', 'state' }

bridge = provision('user-123', 'https://yourbackend.com/hook')
print(bridge)
← response
{"id": "7d09aa9c-…", "customer_ref": "user-123", "state": "running", "created_at": 1777180605, "webhook_secret": "whs_…", "webhook_url": "https://…/hook"}
3
Pair a phone
A code, about a minute

You need the phone in hand for this step. Request a pairing code, open WhatsApp on the phone, go to Linked Devices → Link a device → Link with phone number, then enter the code. Any number works, personal, VoIP, or a spare SIM. No phone? Use sandbox.

Once paired, the bridge fires a connected webhook event. Poll /proxy/status or wait for the event.

python
def pair(customer_ref, phone):
    r = requests.post(
        f'{BASE}/instances/{customer_ref}/proxy/pair',
        json={'phone': phone},
        headers=HEADERS
    )
    r.raise_for_status()
    return r.json()['code']  # "ABCD-EFGH"

code = pair('user-123', '15550001234')
print(f'Enter this code on the phone: {code}')
← response
{"code": "ABCD-EFGH"}
4
Send a message
Digits only, no plus sign

Add an Idempotency-Key: <unique-per-message> header in production so a timeout plus retry can never double-send. See Idempotency.

A bridge that sat for a few hours with no phone linked is paused to save resources. The first call after that returns 503 bridge_starting with a Retry-After header. Wait those seconds and retry once.

python
def send_text(customer_ref, to, body):
    r = requests.post(
        f'{BASE}/instances/{customer_ref}/proxy/send/text',
        json={'chat': to, 'body': body},
        headers=HEADERS
    )
    r.raise_for_status()
    return r.json()  # { 'message_id', 'timestamp' }

send_text('user-123', '15559876543', 'Hello from the API!')
← response
{"message_id": "ACE41E...", "timestamp": 1777180605}
Receive events
No polling, ever

Inbound messages and events are delivered by POST to your webhook_url. Return 200 immediately and do the work afterwards.

The webhook guide has every event type and field, plus signature verification.

python
from flask import Flask, request
import threading

app = Flask(__name__)

def handle(payload):
    if payload.get('event') == 'message' and not payload.get('from_me'):
        print(f"{payload['name']}: {payload['body']}")

@app.route('/hook', methods=['POST'])
def webhook():
    payload = request.get_json()
    threading.Thread(target=handle, args=(payload,)).start()
    return '', 200