In a Sendgo integration the code is the last step. Before it comes account setup: picking the organisation, getting a key, registering the sender number and the template. Until now all of that meant clicking through a browser.

Not any more. **Everything except topping up credit is an API call**, and a coding agent can make it.

> A human does exactly one thing: issue an agent token in the console and export it. An application key cannot create itself, so this step cannot be removed.

## Step 1 — Issue an agent token (the only manual step)

Sign in to the [Sendgo console](https://sendgo.io) and go to **Integration → AI agent tokens**. Pick a name and the scopes; the token is shown once.

There are six scopes.

| Scope | What it allows |
| --- | --- |
| `account:read` | Read organisations, keys, sender numbers, templates, balance |
| `keys:write` | Issue and revoke application keys, manage the IP allow-list |
| `senders:write` | Register SMS sender numbers, link Kakao channels |
| `templates:write` | Register Alimtalk / Brand Message / SMS templates, request inspection |
| `ops:write` | Webhook subscription, short URLs, image upload |
| `messages:send` | Actually send (consumes credit) |

The default is **everything except `messages:send`** — delegating setup while keeping a human in the loop for sending is the most common arrangement.

Put the value in the environment. Do not commit it.

```sh
export SENDGO_AGENT_TOKEN="the-token-you-were-shown"
```

## Step 2 — Register the MCP server

If your agent speaks MCP (Model Context Protocol), one URL is the whole setup.

Claude Code:

```sh
claude mcp add --transport http sendgo https://sendgo.io/mcp \
  --header "Authorization: Bearer $SENDGO_AGENT_TOKEN"
```

Cursor, Windsurf and other MCP clients take the same thing as configuration:

```json
{
  "mcpServers": {
    "sendgo": {
      "type": "http",
      "url": "https://sendgo.io/mcp",
      "headers": {
        "Authorization": "Bearer ${SENDGO_AGENT_TOKEN}"
      }
    }
  }
}
```

Two things arrive together:

- **Tools** — every endpoint of the public API. The tool list is generated from `openapi.yaml`, so it cannot drift from the spec. Tools your token has no scope for are not listed at all.
- **Resources** — all twenty SDK guides, this whole cookbook, and the agent rules file (`sendgo://rules`). The agent reads per-language idioms from here instead of searching the web.

Without MCP, use [the REST calls in step 3](#step-3-select-an-organisation-and-issue-a-key) directly.

## Step 3 — Select an organisation and issue a key

The first call to make is **where do I stand**.

```sh
curl -s https://sendgo.io/api/v2/account \
  -H "Authorization: Bearer $SENDGO_AGENT_TOKEN"
```

In MCP that is the `get_account` tool. The response carries `nextSteps`: plain sentences describing what is actually blocking progress — an organisation awaiting business approval, a key awaiting operator approval, a missing token scope. Those are things code cannot fix, so the agent should relay them to the user verbatim.

List the organisations and pick one.

```sh
curl -s https://sendgo.io/api/v2/account/organizations \
  -H "Authorization: Bearer $SENDGO_AGENT_TOKEN"

curl -s -X POST https://sendgo.io/api/v2/account/organizations/select \
  -H "Authorization: Bearer $SENDGO_AGENT_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"organizationId":"organisation-uuid"}'
```

> **Kakao requires a team organisation.** Alimtalk and Brand Message only work with an approved, team-owned application. A personal account can send SMS only, so check `kakaoAvailable` in the organisation list first.

Issue the key.

```sh
curl -s -X POST https://sendgo.io/api/v2/account/api-keys \
  -H "Authorization: Bearer $SENDGO_AGENT_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"name":"production"}'
```

The full `secretKey` appears in **this response only**. The agent should write it straight into `.env`:

```
SENDGO_ACCESS_KEY=...
SENDGO_SECRET_KEY=...
```

Depending on site configuration the new key may start as `PENDING`, in which case token issuance is refused until an operator approves it — **issued does not mean usable.**

If you are going to restrict by IP, there is one trap:

```sh
curl -s -X POST https://sendgo.io/api/v2/account/api-keys/{apiKeyId}/allowed-ips \
  -H "Authorization: Bearer $SENDGO_AGENT_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"ip":"203.0.113.7","description":"production egress"}'
```

> **Registering the first IP turns the restriction on.** Before that there was none; after it, every IP not on the list is rejected with `IP_NOT_ALLOWED`. This is where "it worked on my laptop and died in production" usually comes from. The list response includes `callerIp` — the address the call actually arrived from.

## Step 4 — Register the sender number and the template

Korean law requires the caller ID to be registered in advance. Read the number types and the documents each one needs first.

```sh
curl -s https://sendgo.io/api/v2/senders/number-types \
  -H "Authorization: Bearer $ACCESS_TOKEN"
```

Then file it with `POST /api/v2/senders`. The full procedure is in [Register a sender number](/en/cookbook/sender-number).

Alimtalk templates are registered with `POST /api/v2/notice-templates` and submitted with `POST /api/v2/notice-templates/{templateCode}/inspection`. Registration, edits and inspection cancellation are all API calls — see [Move management work to the API](/en/cookbook/manage-by-api).

> **Registering is filing, not approving.** Sender numbers are reviewed by a Sendgo operator, templates by Kakao. An agent that chains a send onto a registration and reports "done" is reporting something false. Subscribe to the webhook instead.

```sh
curl -s -X PUT https://sendgo.io/api/v2/webhook \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"url":"https://example.com/hooks/sendgo","enabled":true}'
```

## Step 5 — Send

Once the reviews clear, send. Have the agent read the SDK guide for its own language before writing code — naming differs per SDK (`templateCode` vs `template_code`) and guessing produces code that does not compile. Over MCP that is a resource read (`sendgo://sdk/laravel`); otherwise fetch `https://sendgo.io/en/sdk/laravel.md`.

The send itself is the same code as in [Send an Alimtalk](/en/cookbook/send-alimtalk) and [Send an SMS](/en/cookbook/send-sms).

## Handing the agent a rules file

Even without MCP you can make an agent write correct Sendgo code by committing the rules file into your repository.

```sh
curl -o AGENTS.md https://sendgo.io/sendgo-rules.md
```

Claude Code reads `CLAUDE.md`, Cursor reads `.cursor/rules/sendgo.mdc`, Copilot reads `.github/copilot-instructions.md`. The file is generated from the catalogs, so it always matches what is actually published. [The agent setup page](/en/ai) lists the path for each tool.

## What is not available

| Task | Where |
| --- | --- |
| List and switch organisations | API · MCP |
| Issue / revoke keys, manage IP allow-list | API · MCP |
| Register sender numbers, file for review | API · MCP |
| Link Kakao channels, request the auth code | API · MCP |
| Register / edit templates, request inspection | API · MCP |
| Send, look up results, read the balance | API · MCP |
| Webhook subscription, short URLs | API · MCP |
| **Topping up credit (payment)** | **Console only** |
| Image file upload | API (multipart) — not exposed as an MCP tool |

The Kakao channel auth code is an SMS Kakao sends to the channel administrator's phone. The API can request it, but a human has to read and enter it — that check belongs to Kakao and cannot be removed.