Bulk Alimtalk sending and per-recipient variables

Bulk Alimtalk sending and per-recipient variables

Send one template to many recipients with different values each, in batches. Batch sizing, partial failures, queue patterns and the data hygiene that prevents most incidents.

POST /api/v2/notices/send

Bulk sending is one template, many values. Each entry in contacts carries its own variables, so the same wording can deliver a different order number, amount or date to each person.

The basic shape

await sendgo.alimtalk.send({
  templateCode: 'ORDER_CONFIRM_001',
  contacts: [
    { contact: '01011111111', name: 'Hong Gildong', var1: 'ORD-001', var2: '29,000원' },
    { contact: '01022222222', name: 'Kim Chulsoo',  var1: 'ORD-002', var2: '15,000원' },
    { contact: '01033333333', name: 'Lee Younghee', var1: 'ORD-003', var2: '52,000원' },
  ],
});
client.alimtalk.send(
    template_code="ORDER_CONFIRM_001",
    contacts=[
        {"contact": "01011111111", "name": "Hong Gildong", "var1": "ORD-001"},
        {"contact": "01022222222", "name": "Kim Chulsoo", "var1": "ORD-002"},
    ],
)
<?php

$sendgo->alimtalk->send([
    'templateCode' => 'ORDER_CONFIRM_001',
    'contacts'     => [
        ['contact' => '01011111111', 'name' => 'Hong Gildong', 'var1' => 'ORD-001'],
        ['contact' => '01022222222', 'name' => 'Kim Chulsoo', 'var1' => 'ORD-002'],
    ],
]);

Batching

Ten thousand recipients in one request means a timeout tells you nothing about how far it got, and retrying is expensive. Split into a few hundred at a time.

Node.js

const BATCH = 500;

async function sendInBatches(recipients: Recipient[]) {
  const failures: Recipient[] = [];

  for (let i = 0; i < recipients.length; i += BATCH) {
    const chunk = recipients.slice(i, i + BATCH);

    try {
      await sendgo.alimtalk.send({
        templateCode: 'ORDER_CONFIRM_001',
        contacts: chunk.map((r) => ({
          contact: r.phone,
          name: r.name,
          var1: r.orderNo,
          var2: r.amount,
        })),
      });
    } catch (error) {
      // One bad batch must not stop the rest.
      console.error(`batch ${i / BATCH} failed`, error);
      failures.push(...chunk);
    }
  }

  return failures;
}

PHP · Laravel

<?php

use Illuminate\Support\Collection;

collect($recipients)->chunk(500)->each(function (Collection $chunk) use ($sendgo) {
    try {
        $sendgo->alimtalk->send([
            'templateCode' => 'ORDER_CONFIRM_001',
            'contacts'     => $chunk->map(fn ($r) => [
                'contact' => $r->phone,
                'name'    => $r->name,
                'var1'    => $r->order_no,
                'var2'    => number_format($r->amount).'원',
            ])->values()->all(),
        ]);
    } catch (\Sendgo\Php\Exception\SendgoException $e) {
        Log::error('batch send failed', ['message' => $e->getMessage()]);
    }
});

Python

BATCH = 500

for i in range(0, len(recipients), BATCH):
    chunk = recipients[i:i + BATCH]
    try:
        client.alimtalk.send(
            template_code="ORDER_CONFIRM_001",
            contacts=[{"contact": r.phone, "name": r.name, "var1": r.order_no} for r in chunk],
        )
    except SendgoError as e:
        logger.error("batch send failed: %s", e)

Do it in a queue

Never run a bulk send inside a web request. The user waits, and a timeout leaves no way to resume from the middle.

<?php

namespace App\Jobs;

use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Sendgo\Php\Sendgo;
use Sendgo\Php\Exception\SendgoException;

class SendOrderAlimtalk implements ShouldQueue
{
    use Queueable;

    // Most send failures fail identically on retry. Do not loop forever.
    public int $tries = 3;
    public int $backoff = 30;

    public function __construct(private array $contacts) {}

    public function handle(Sendgo $sendgo): void
    {
        $sendgo->alimtalk->send([
            'templateCode' => 'ORDER_CONFIRM_001',
            'contacts'     => $this->contacts,
        ]);
    }

    public function failed(SendgoException $e): void
    {
        // Recording the failed batch is what lets you resend just the failures.
        FailedDispatch::create(['contacts' => $this->contacts, 'reason' => $e->getMessage()]);
    }
}

Partial failures

A 200 response does not mean every recipient received it. Numbers can be dead, recipients blocked, people not on KakaoTalk.

  • Do not resend the whole batch — everyone who succeeded gets it twice.
  • Resend only the failures, or enable SMS fallback so they roll over to text automatically.
  • Per-recipient outcomes are in the console's send history.

Easy to get wrong

  • Normalise numbers first. Real databases contain 010-1234-5678, +821012345678 and 01012345678 side by side. Strip hyphens and country codes down to digits.
  • Deduplicate. A repeated number sends twice and bills twice.
  • Check the credit balance up front. Running out midway through 10,000 messages returns PAYMENT_REQUIRED and the remainder fails wholesale.
  • Advertising content is subject to the night ban. Make sure a batch cannot run past 21:00 KST → Advertising message rules

Next

자주 묻는 질문

How many recipients can one request take?
The contacts array can hold many recipients in a single request. Very large requests are risky though — a timeout leaves you unsure how much was delivered — so batches of a few hundred are more stable.
Can each recipient get different values?
Yes. Every entry in contacts carries its own var1 to var8. Sending the same template with a different order number and amount per person is the normal use.
What if only some recipients fail?
A request can succeed while individual recipients fail — invalid numbers, blocked recipients. Check per-recipient results in the console and resend only the failures; resending the whole batch double-sends to everyone who succeeded.
Can I run a bulk send inside a web request?
Not advisable. The external call lands in your response time, and on timeout you cannot tell how far it got. Move it to a queue job.

이 문서에서 쓰는 패키지

관련 문서