> **The pure-PHP SDK for sending Kakao Alimtalk, Brand Message and SMS**

`sendgo/php` is the **framework-agnostic PHP SDK** for the [Sendgo](https://sendgo.io) messaging API.
It does not depend on Laravel or any other framework — `ext-curl` and `ext-json` are all it needs.

---

## Install

```bash
composer require sendgo/php
```

---

## Quick start

```php
<?php

use Sendgo\Php\Sendgo;

$sendgo = new Sendgo([
    'access_key'       => $_ENV['SENDGO_ACCESS_KEY'],
    'secret_key'       => $_ENV['SENDGO_SECRET_KEY'],
    'kakao_sender_key' => $_ENV['SENDGO_KAKAO_SENDER_KEY'],
    'sms_sender_key'   => $_ENV['SENDGO_SMS_SENDER_KEY'],
    'api_version'      => 'v2',
]);

// Send an Alimtalk
$sendgo->alimtalk->send([
    'templateCode' => 'ORDER_CONFIRM_001',
    'contacts'     => [
        ['contact' => '01012345678', 'name' => 'Gildong Hong', 'var1' => 'ORD-001', 'var2' => '29,000 KRW'],
    ],
]);

// Send an SMS
$sendgo->sms->sendSms([
    'content'  => '[Sendgo] Your code is 123456 (valid for 5 minutes)',
    'contacts' => [['contact' => '01012345678']],
]);
```

Services are reachable as properties (`$sendgo->alimtalk`) or as methods (`$sendgo->alimtalk()`) — both return the same instance.

---

## Alimtalk in detail

```php
<?php

// Multiple recipients
$sendgo->alimtalk->send([
    'templateCode' => 'ORDER_CONFIRM_001',
    'contacts'     => [
        ['contact' => '01011111111', 'name' => 'Gildong Hong', 'var1' => 'ORD-001', 'var2' => '29,000 KRW'],
        ['contact' => '01022222222', 'name' => 'Chulsoo Kim', 'var1' => 'ORD-002', 'var2' => '15,000 KRW'],
    ],
]);

// Scheduled send
$sendgo->alimtalk->send([
    'templateCode' => 'PROMO_SUMMER_2026',
    'scheduleType' => 'SCHEDULED',
    'at'           => '2026-07-28 09:00:00',
    'contacts'     => [['contact' => '01012345678', 'var1' => 'Summer sale — 50% off']],
]);

// Fall back to SMS when the Alimtalk fails
$sendgo->alimtalk->send([
    'templateCode' => 'DELIVERY_START_001',
    'replaceSms'   => 'Y',
    'smsSubject'   => '[Shipping notice]',
    'smsContent'   => "Your order has shipped.\nTracking: #{var2}",
    'contacts'     => [['contact' => '01012345678', 'var1' => 'ORD-001', 'var2' => '1234567890']],
]);
```

---

## Friendtalk

> ⚠️ **Deprecated — Friendtalk was discontinued on 2025-12-31 under Kakao's policy.**
> Since 2026-01-01, Friendtalk send requests are automatically delivered as
> **Brand Message (free-form)** by Kakao. Calls still succeed, and this is still the
> only path for free-form types (`FT`/`FI`/`FW`) sent to individual recipients, so
> there is no need to change working code right now.
>
> Use **Brand Message** instead for:
> - template-based rich types (`FL`/`FC`/`FM`/`FP`/`FA`)
> - recipients who are **not** channel friends (`targeting` = `N` / `I`)
> - broadcasts to every opted-in channel friend (`targeting` = `F`)
>
> Message types map one-to-one and the server does the conversion — `FT`→`BT`,
> `FI`→`BI`, `FW`→`BW`, `FL`→`BL`, `FC`→`BC`, `FM`→`BM`, `FP`→`BP`, `FA`→`BA`.
```php
<?php

// Text
$sendgo->friendtalk->send([
    'content'  => 'Hello! Check out this month\'s deals.',
    'contacts' => [['contact' => '01012345678']],
]);

// Image
$sendgo->friendtalk->send([
    'messageType' => 'FI',
    'content'     => 'This week\'s featured products.',
    'imageUrl'    => 'https://cdn.example.com/banner.jpg',
    'imageLink'   => 'https://example.com/event',
    'contacts'    => [['contact' => '01012345678']],
]);

// With buttons
$sendgo->friendtalk->send([
    'content'  => 'Your coupon has arrived. Use it now!',
    'buttons'  => [
        ['name' => 'Get coupon', 'type' => 'WL', 'linkMo' => 'https://example.com/coupon'],
    ],
    'contacts' => [['contact' => '01012345678']],
]);
```

---

## Brand Message

Brand Message is the successor channel to Friendtalk. Message types map one-to-one
(`FT`→`BT`, `FI`→`BI`, `FW`→`BW`, `FL`→`BL`, `FC`→`BC`, `FM`→`BM`, `FP`→`BP`, `FA`→`BA`);
pass the **Friendtalk code** and the server converts it.

Unlike Friendtalk it can also:

- reach recipients who are **not channel friends** (`targeting: N`)
- **broadcast to every consenting channel friend** (`targeting: F`, no recipient list needed)
- send **template-based rich messages** — lists, carousels, commerce, video

> v2 only. For plain text or images (`FT`/`FI`/`FW`) to channel friends, the Friendtalk API is simpler.

```php
<?php

// Single send — channel friends
$sendgo->brandMessage->send([
    'targeting'          => 'M',
    'messageType'        => 'FL',
    'friendTemplateUuid' => '9cd5460b-6458-4edc-9b11-c26d3013c340',
    'contacts'           => [['contact' => '01012345678', 'var1' => '29,000 KRW']],
]);

// Broadcast — every consenting channel friend (no contacts)
$sendgo->brandMessage->broadcast([
    'messageType'        => 'FW',
    'friendTemplateUuid' => '9cd5460b-6458-4edc-9b11-c26d3013c340',
]);

// Campaign lookups
$list = $sendgo->brandMessage->campaigns(['count' => 10]);
$one  = $sendgo->brandMessage->campaign('1f0a6d0e-6b3b-4f0f-9b2f-2f6f6a1b7c11');
```

---

## SMS / LMS / MMS

```php
<?php

// SMS (up to 90 bytes)
$sendgo->sms->sendSms([
    'content'  => '[Sendgo] Your code is 123456 (valid for 5 minutes)',
    'contacts' => [['contact' => '01012345678']],
]);

// LMS (long text, up to 2,000 bytes)
$sendgo->sms->sendLms([
    'subject'  => '[Important] Scheduled maintenance',
    'content'  => "Maintenance is scheduled.\n\n■ When: 2026-07-25 02:00–06:00\n■ Impact: all services",
    'contacts' => [['contact' => '01012345678']],
]);

// MMS (with an image)
$sendgo->sms->sendMms([
    'subject'  => '[Event] July deals',
    'content'  => 'Check out this month\'s deals!',
    'contacts' => [['contact' => '01012345678']],
]);

// Scheduled SMS
$sendgo->sms->sendSms([
    'content'      => '[Reminder] Please confirm your appointment.',
    'scheduleType' => 'SCHEDULED',
    'at'           => '2026-07-23 08:00:00',
    'contacts'     => [['contact' => '01012345678']],
]);
```

---

## Framework integration

### Symfony

```php
<?php
// src/Service/NotificationService.php

namespace App\Service;

use Sendgo\Php\Sendgo;

class NotificationService
{
    public function __construct(private Sendgo $sendgo) {}

    public function sendOrderConfirm(string $phone, string $orderNo): void
    {
        $this->sendgo->alimtalk->send([
            'templateCode' => 'ORDER_CONFIRM_001',
            'contacts'     => [['contact' => $phone, 'var1' => $orderNo]],
        ]);
    }
}
```

For Symfony there is also a dedicated bundle, `sendgo/symfony`, which registers the client for you.

### Slim Framework

```php
<?php
// bootstrap/app.php

use DI\Container;
use Sendgo\Php\Sendgo;

$container = new Container();
$container->set(Sendgo::class, fn() => new Sendgo([
    'access_key'       => $_ENV['SENDGO_ACCESS_KEY'],
    'secret_key'       => $_ENV['SENDGO_SECRET_KEY'],
    'kakao_sender_key' => $_ENV['SENDGO_KAKAO_KEY'],
    'api_version'      => 'v2',
]));
```

### WordPress / WooCommerce

```php
<?php

use Sendgo\Php\Sendgo;
use Sendgo\Php\Exception\SendgoException;

function get_sendgo(): Sendgo {
    static $instance = null;
    if ($instance === null) {
        $instance = new Sendgo([
            'access_key'       => get_option('sendgo_access_key'),
            'secret_key'       => get_option('sendgo_secret_key'),
            'kakao_sender_key' => get_option('sendgo_kakao_key'),
            'api_version'      => 'v2',
        ]);
    }
    return $instance;
}

// Send an Alimtalk when a WooCommerce order completes
add_action('woocommerce_order_status_completed', function (int $orderId) {
    $order = wc_get_order($orderId);
    try {
        get_sendgo()->alimtalk->send([
            'templateCode' => 'ORDER_CONFIRM_001',
            'contacts'     => [
                ['contact' => $order->get_billing_phone(), 'var1' => $order->get_order_number()],
            ],
        ]);
    } catch (SendgoException $e) {
        error_log("Sendgo notification failed: {$e->getMessage()}");
    }
});
```

A ready-made plugin, `sendgo/wordpress`, is also available.

---

## Error handling

```php
<?php

use Sendgo\Php\Exception\SendgoException;

try {
    $sendgo->alimtalk->send([
        'templateCode' => 'ORDER_CONFIRM_001',
        'contacts'     => [['contact' => '01012345678']],
    ]);
} catch (SendgoException $e) {
    echo "Send failed: HTTP {$e->getStatusCode()} [{$e->getErrorCode()}]".PHP_EOL;

    match ($e->getErrorCode()) {
        'INVALID_ACCESS_KEY',
        'INVALID_SECRET_KEY'    => alertOps('Check the Sendgo API keys.'),
        'INVALID_TEMPLATE_CODE' => logger('Unknown template'),
        'PAYMENT_REQUIRED'      => alertOps('Out of Sendgo credit.'),
        'IP_NOT_ALLOWED'        => alertOps('IP is not allow-listed.'),
        default                 => logger('Unexpected error: '.$e->getMessage()),
    };
}
```

Branch on `getErrorCode()`, not on the message text — messages can change, codes are the contract.

---

## Configuration options

| Parameter | Type | Required | Default | Description |
|-----------|------|----------|---------|-------------|
| `access_key` | `string` | **required** | — | Sendgo access key |
| `secret_key` | `string` | **required** | — | Sendgo secret key |
| `kakao_sender_key` | `string\|null` | optional | `null` | Kakao sender profile key |
| `sms_sender_key` | `string\|null` | optional | `null` | SMS caller ID key |
| `api_version` | `string` | optional | `'v1'` | API version (`v1` \| `v2`) |
| `url` | `string` | optional | `'https://sendgo.io'` | API base URL |

---

## Short URL

Short URLs shrink the links in your message body and count whether they were
actually clicked. SMS is billed by byte, so a shorter link leaves more room for copy.

> v2 only.

Shortening the same target URL again **returns the existing link**. Pass `forceNew`
to mint a new code when you want per-campaign reaction figures kept separate.

`deactivate` does not delete the link — it only stops the redirect. Use it when a link
in an already-sent message has to be killed; the accumulated stats stay, and visitors
to a stopped link get `410 Gone`.

```php
// Create a short URL
$short = $sendgo->shortUrl->create([
    'targetUrl' => 'https://example.com/promotions/summer-sale',
    'title'     => 'Summer sale landing',
]);

// The short link to drop into the message body
$link = $short['data']['shortUrl'];
$code = $short['data']['code'];

// Reaction stats — daily series + device / referrer / country breakdowns
$stats = $sendgo->shortUrl->stats($code, ['from' => '2026-08-01']);

$sendgo->shortUrl->list(['count' => 10]);
$sendgo->shortUrl->show($code);
$sendgo->shortUrl->deactivate($code);   // Stops the redirect only; the stats stay
```

`stats` returns a daily series (`daily`) plus breakdowns by device (`byDevice`), referrer (`byReferer`) and country (`byCountry`). The daily series is read from a pre-aggregated table, so response time stays flat no matter how many clicks accumulate.

---

## Package information

- **Package**: `sendgo/php` (Packagist)
- **Repository**: [send-go/php](https://github.com/send-go/php)
- **Registry**: https://packagist.org/packages/sendgo/php
- **License**: MIT

### Getting your API keys

Sign in to Sendgo and open **API/SDK → API integration** to issue an access key and secret key.
Register a Kakao sender profile under **Kakao channel** to get your `kakao_sender_key`, and a caller ID under **Sender numbers** for SMS.