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 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.