Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions apps/docs/docs.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
"introduction",
"get-started/nodejs",
"get-started/python",
"get-started/php",
"get-started/local",
"get-started/smtp"
]
Expand Down
105 changes: 105 additions & 0 deletions apps/docs/get-started/php.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
---
title: PHP (API)
description: "Use the REST API to send emails and manage contacts from PHP."
icon: php
---

useSend doesn't ship an official PHP SDK yet, but you can call the REST API with any HTTP client. The examples below use [Guzzle](https://github.com/guzzle/guzzle).

## Prerequisites

- [useSend API key](https://app.usesend.com/dev-settings/api-keys)
- [Verified domain](https://app.usesend.com/domains)
- PHP 8.1+ with Composer

## Install an HTTP client

```bash
composer require guzzlehttp/guzzle
```

## Configure a reusable client

```php
<?php
require __DIR__ . '/vendor/autoload.php';

use GuzzleHttp\\Client;
Copy link

@cubic-dev-ai cubic-dev-ai bot Nov 29, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

use statement shows a double backslash, so copying the snippet yields invalid PHP syntax. Use a single namespace separator when importing GuzzleHttp\Client.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/docs/get-started/php.mdx, line 27:

<comment>`use` statement shows a double backslash, so copying the snippet yields invalid PHP syntax. Use a single namespace separator when importing `GuzzleHttp\Client`.</comment>

<file context>
@@ -0,0 +1,105 @@
+&lt;?php
+require __DIR__ . &#39;/vendor/autoload.php&#39;;
+
+use GuzzleHttp\\Client;
+
+$apiKey = getenv(&#39;USESEND_API_KEY&#39;);
</file context>
Suggested change
use GuzzleHttp\\Client;
+use GuzzleHttp\Client;
Fix with Cubic


$apiKey = getenv('USESEND_API_KEY');

$client = new Client([
'base_uri' => 'https://app.usesend.com/api/',
'headers' => [
'Authorization' => "Bearer {$apiKey}",
'Content-Type' => 'application/json',
],
]);

// Self-hosted example:
// $client = new Client(['base_uri' => 'https://your-self-hosted.example/api/', ...]);
```

## Send an email

```php
<?php
$response = $client->post('v1/emails', [
'json' => [
'to' => '[email protected]',
'from' => '[email protected]',
'subject' => 'Welcome to useSend',
'html' => '<p>useSend is the best open source product to send emails</p>',
'text' => 'useSend is the best open source product to send emails',
'headers' => [
'X-Campaign' => 'welcome',
],
],
]);

$body = json_decode($response->getBody()->getContents(), true);
```

> Custom headers are forwarded as-is. useSend only manages the `X-Usesend-Email-ID` and `References` headers.

## Create or update contacts

All contact operations require a contact book ID from the [Contacts dashboard](https://app.usesend.com/contacts/).

```php
<?php
$bookId = 'contact_book_id';

// Create a contact
$createResponse = $client->post("v1/contactBooks/{$bookId}/contacts", [
'json' => [
'email' => '[email protected]',
'firstName' => 'Ada',
'properties' => ['plan' => 'pro'],
],
]);

// Update a contact
$contactId = 'contact_123';
$updateResponse = $client->patch("v1/contactBooks/{$bookId}/contacts/{$contactId}", [
'json' => [
'subscribed' => false,
],
]);
```

## Basic error handling

```php
<?php
try {
$response = $client->get('v1/emails/email_123');
$email = json_decode($response->getBody()->getContents(), true);
} catch (\\GuzzleHttp\\Exception\\ClientException $e) {
Copy link

@cubic-dev-ai cubic-dev-ai bot Nov 29, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ClientException catch statement escapes the namespace separator twice, making the provided PHP snippet uncompilable. Use single backslashes in the fully qualified class name.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/docs/get-started/php.mdx, line 98:

<comment>ClientException catch statement escapes the namespace separator twice, making the provided PHP snippet uncompilable. Use single backslashes in the fully qualified class name.</comment>

<file context>
@@ -0,0 +1,105 @@
+try {
+    $response = $client-&gt;get(&#39;v1/emails/email_123&#39;);
+    $email = json_decode($response-&gt;getBody()-&gt;getContents(), true);
+} catch (\\GuzzleHttp\\Exception\\ClientException $e) {
+    // 4xx responses
+    error_log($e-&gt;getResponse()-&gt;getBody()-&gt;getContents());
</file context>
Suggested change
} catch (\\GuzzleHttp\\Exception\\ClientException $e) {
+} catch (\GuzzleHttp\Exception\ClientException $e) {
Fix with Cubic

// 4xx responses
error_log($e->getResponse()->getBody()->getContents());
} catch (\\GuzzleHttp\\Exception\\ServerException $e) {
Copy link

@cubic-dev-ai cubic-dev-ai bot Nov 29, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ServerException catch block double-escapes the namespace separator, so the example cannot be pasted into PHP as-is. Replace it with a single backslash-qualified class name.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/docs/get-started/php.mdx, line 101:

<comment>ServerException catch block double-escapes the namespace separator, so the example cannot be pasted into PHP as-is. Replace it with a single backslash-qualified class name.</comment>

<file context>
@@ -0,0 +1,105 @@
+} catch (\\GuzzleHttp\\Exception\\ClientException $e) {
+    // 4xx responses
+    error_log($e-&gt;getResponse()-&gt;getBody()-&gt;getContents());
+} catch (\\GuzzleHttp\\Exception\\ServerException $e) {
+    // 5xx responses
+    error_log(&#39;Server error: &#39; . $e-&gt;getMessage());
</file context>
Suggested change
} catch (\\GuzzleHttp\\Exception\\ServerException $e) {
+} catch (\GuzzleHttp\Exception\ServerException $e) {
Fix with Cubic

// 5xx responses
error_log('Server error: ' . $e->getMessage());
}
```