PHP, Python, and API clients
Wabery’s REST API works from any HTTP client. The TypeScript SDK is optional: PHP and Python applications use the same endpoints, JSON bodies, authentication, idempotency keys, and response objects.
Shared request contract
Section titled “Shared request contract”Every server-side REST client uses:
Base URL: https://api.wabery.com/v1Authorization: Bearer <WABERY_API_KEY>Content-Type: application/jsonKeep the secret key in an environment variable:
export WABERY_API_KEY="wab_live_..."$env:WABERY_API_KEY = "wab_live_..."Use your preferred API client
Section titled “Use your preferred API client”Wabery publishes a standard OpenAPI 3.1 description containing every public operation, request schema, authentication requirement, and response schema:
https://api.wabery.com/v1/openapi.jsonIn any OpenAPI-compatible client:
- Choose Import, OpenAPI, or Import from URL.
- Paste the URL above.
- Select
https://api.wabery.com/v1as the server if prompted. - Configure Bearer authentication with your
WABERY_API_KEY. - Choose an operation, replace placeholder resource IDs, and send the request.
This works with clients that import OpenAPI 3.x from a URL, including local, offline, open-source, and hosted options. Wabery does not require or endorse a particular API client.
PHP 8.1 or newer with the cURL extension is sufficient; no Wabery package is required. Put this reusable helper in your server application:
<?php
/** * @return array<string, mixed>|list<mixed>|null */function waberyRequest( string $method, string $path, ?array $body = null,): ?array { $apiKey = getenv("WABERY_API_KEY"); if ($apiKey === false || $apiKey === "") { throw new RuntimeException("WABERY_API_KEY is not set"); }
$url = "https://api.wabery.com/v1/" . ltrim($path, "/"); $handle = curl_init($url); if ($handle === false) { throw new RuntimeException("Could not initialize cURL"); }
$headers = [ "Accept: application/json", "Authorization: Bearer " . $apiKey, ]; if ($body !== null) { $headers[] = "Content-Type: application/json"; }
$options = [ CURLOPT_CUSTOMREQUEST => strtoupper($method), CURLOPT_RETURNTRANSFER => true, CURLOPT_CONNECTTIMEOUT => 10, CURLOPT_TIMEOUT => 30, CURLOPT_HTTPHEADER => $headers, ];
if ($body !== null) { $options[CURLOPT_POSTFIELDS] = json_encode( $body, JSON_THROW_ON_ERROR | JSON_UNESCAPED_SLASHES, ); }
if (!curl_setopt_array($handle, $options)) { throw new RuntimeException("Could not configure the Wabery request"); }
$responseBody = curl_exec($handle); if ($responseBody === false) { throw new RuntimeException("Wabery request failed: " . curl_error($handle)); }
$status = curl_getinfo($handle, CURLINFO_RESPONSE_CODE); if ($status < 200 || $status >= 300) { throw new RuntimeException( "Wabery API returned HTTP {$status}: {$responseBody}", ); }
if ($responseBody === "") { return null; }
return json_decode($responseBody, true, 512, JSON_THROW_ON_ERROR);}Send a text reply:
<?php
require __DIR__ . "/wabery.php";
$message = waberyRequest("POST", "/messages", [ "channel_id" => "channel_...", "conversation_id" => "conversation_...", "text" => "Thanks for your message",]);
echo $message["id"] . PHP_EOL;Send an image from a public HTTPS URL:
<?php
require __DIR__ . "/wabery.php";
$message = waberyRequest("POST", "/messages", [ "channel_id" => "channel_...", "conversation_id" => "conversation_...", "idempotency_key" => "reply-image-123", "media" => [ "type" => "image", "link" => "https://cdn.example.com/photo.jpg", ],]);Read a conversation’s messages:
<?php
require __DIR__ . "/wabery.php";
$result = waberyRequest( "GET", "/conversations/conversation_.../messages?order=asc&limit=100",);
foreach ($result["data"] as $message) { echo ($message["content"] ?? "[media]") . PHP_EOL;}Verify webhooks in PHP
Section titled “Verify webhooks in PHP”Verify the signature against the exact raw request body before decoding JSON:
<?php
$rawBody = file_get_contents("php://input");$signature = $_SERVER["HTTP_X_WABERY_SIGNATURE"] ?? "";$secret = getenv("WABERY_WEBHOOK_SECRET");
if ($rawBody === false || $secret === false || $secret === "") { http_response_code(500); exit;}
$expected = "sha256=" . hash_hmac("sha256", $rawBody, $secret);if (!hash_equals($expected, $signature)) { http_response_code(401); exit;}
$event = json_decode($rawBody, true, 512, JSON_THROW_ON_ERROR);
// Queue or process $event, then acknowledge promptly.http_response_code(204);Python
Section titled “Python”Install the Requests package:
python -m pip install requestsCreate one reusable client:
import osfrom typing import Any
import requests
BASE_URL = "https://api.wabery.com/v1"API_KEY = os.environ["WABERY_API_KEY"]
session = requests.Session()session.headers.update( { "Accept": "application/json", "Authorization": f"Bearer {API_KEY}", })
def wabery_request( method: str, path: str, *, json: dict[str, Any] | None = None, params: dict[str, Any] | None = None,) -> Any: response = session.request( method, f"{BASE_URL}/{path.lstrip('/')}", json=json, params=params, timeout=(10, 30), )
try: response.raise_for_status() except requests.HTTPError as error: raise RuntimeError( f"Wabery API returned HTTP {response.status_code}: {response.text}" ) from error
return response.json() if response.content else NoneSend a text reply:
from wabery import wabery_request
message = wabery_request( "POST", "/messages", json={ "channel_id": "channel_...", "conversation_id": "conversation_...", "text": "Thanks for your message", },)
print(message["id"])Send an image from a public HTTPS URL:
from wabery import wabery_request
message = wabery_request( "POST", "/messages", json={ "channel_id": "channel_...", "conversation_id": "conversation_...", "idempotency_key": "reply-image-123", "media": { "type": "image", "link": "https://cdn.example.com/photo.jpg", }, },)Read a conversation’s messages:
from wabery import wabery_request
result = wabery_request( "GET", "/conversations/conversation_.../messages", params={"order": "asc", "limit": 100},)
for message in result["data"]: print(message.get("content") or "[media]")Verify webhooks in Python
Section titled “Verify webhooks in Python”Pass the exact raw bytes supplied by your web framework:
import hashlibimport hmac
def verify_wabery_signature( raw_body: bytes, signature: str, secret: str,) -> bool: digest = hmac.new( secret.encode("utf-8"), raw_body, hashlib.sha256, ).hexdigest() expected = f"sha256={digest}" return hmac.compare_digest(expected, signature)Only parse the event after verify_wabery_signature(...) returns True. See
Webhooks and events for payloads, retries, and media downloads.
Request bodies are JSON
Section titled “Request bodies are JSON”For REST calls, the body shown in documentation is ordinary JSON:
{ "channel_id": "channel_...", "conversation_id": "conversation_...", "text": "Thanks for your message"}The surrounding PHP, Python, cURL, or graphical API client supplies the HTTP method,
URL, Bearer authentication, and Content-Type. JSON by itself is not a complete
API request.
Response and retry behavior
Section titled “Response and retry behavior”- Successful sends return
202 Accepted; delivery remains asynchronous. - Treat
429and transient5xxresponses as retryable with backoff. - Supply a stable
Idempotency-Keyheader oridempotency_keyfor retried message sends. - Do not retry validation or authentication failures without changing the request.
- Use message status webhooks or
GET /v1/messages/{message_id}to observe final delivery.