---
title: "Quickstart"
canonical: "https://test.abhinandan.one/quickstart-369b6942"
markdown_url: "https://test.abhinandan.one/quickstart-369b6942.md"
publisher: "Primitive SDKs"
kind: "quickstart"
content_type: "reference"
category: "Getting Started"
description: "Install @primitivedotdev/sdk, primitivedotdev, sdk-go, or the primitive CLI, set an API key, then receive and reply to an inbound email."
keywords: ["primitive.receive", "primitive.client", "PRIMITIVE_API_KEY", "PRIMITIVE_WEBHOOK_SECRET", "npm install @primitivedotdev/sdk", "npm install -g primitive"]
last_modified: "2026-08-11T18:55:06.312159+00:00"
published_at: "2026-08-11T18:18:13.022006+00:00"
source_files:
  - "README.md"
  - "sdk-node/README.md"
  - "sdk-python/README.md"
  - "sdk-go/README.md"
  - "cli-node/README.md"
sections:
  - {anchor: "step-install", title: "Install"}
  - {anchor: "step-set-your-api-key", title: "Set your API key"}
  - {anchor: "step-receive-and-reply", title: "Receive and reply"}
  - {anchor: "step-send-a-new-email", title: "Send a new email"}
  - {anchor: "escape-hatches", title: "Escape hatches"}
  - {anchor: "next-steps", title: "Next steps"}
---

> Documentation index: https://test.abhinandan.one/llms.txt

# Quickstart

Install a Primitive SDK or the CLI, set your API key, and receive, reply to, and send your first email in minutes.

Primitive is an inbound and outbound email API. This page gets you from a fresh install to a working receive-and-reply loop, using whichever SDK matches your stack, or the CLI if you'd rather work from the terminal.

You need a Primitive API key (`prim_...`) from your dashboard, and, if you're receiving inbound mail, a webhook secret (`whsec_...`) from the same account.

> **Tip:** Building with an AI coding agent? Point it at the [Agent Guide](https://test.abhinandan.one/agent-guide.md) instead, it's a denser, single-page reference built for that workflow.

### 1. Install

Pick your language.

**Choose one of the following:**

**Node.js**

Requires Node.js 22 or newer.

```bash
npm install @primitivedotdev/sdk
```

**Python**

Requires Python 3.10 or newer.

```bash
pip install primitivedotdev
```

**Go**

Requires Go 1.25 or newer.

```bash
go get github.com/primitivedotdev/sdks/sdk-go@latest
```

**CLI**

```bash
npm install -g primitive
# or, no install:
npx primitive@latest <command>
```

The CLI is a separate package from the Node SDK, `@primitivedotdev/sdk` no longer ships a `primitive` bin. Use the CLI for terminal/CI workflows; use an SDK when embedding Primitive in application code.

### 2. Set your API key

Get a key from your dashboard and export it as an environment variable. Every example on this page reads it from there.

```bash
export PRIMITIVE_API_KEY=prim_test
```

If you're receiving inbound mail, also export your webhook secret (used to verify the `Primitive-Signature` header):

```bash
export PRIMITIVE_WEBHOOK_SECRET=whsec_...
```

> **Tip:** Using the CLI instead? Skip the env var and authenticate interactively with `primitive login`, then confirm with `primitive whoami`. See [Authentication: login, signup, logout, whoami](https://test.abhinandan.one/cli-overview/cli-authentication.md) for the full sign-in flow.

### 3. Receive and reply

This is the core loop every SDK is built around: normalize an inbound webhook into a `ReceivedEmail`, then reply to it. See the [Inbound and Outbound Email Model](https://test.abhinandan.one/email-model.md) for what's on that object and how reply threading works.

**Choose one of the following:**

**Node.js**

A Next.js route handler that receives inbound mail and replies:

```ts
import primitive from "@primitivedotdev/sdk";

export const runtime = "nodejs";
export const maxDuration = 300;

const client = primitive.client({
  apiKey: process.env.PRIMITIVE_API_KEY!,
});

export async function POST(req: Request) {
  const email = await primitive.receive(req, {
    secret: process.env.PRIMITIVE_WEBHOOK_SECRET!,
  });

  await client.reply(email, "Thank you for your email.");

  return Response.json({ ok: true });
}
```

`primitive.receive(...)` reads the request body, verifies the HMAC-SHA256 signature against your account secret, and returns a normalized [ReceivedEmail](https://test.abhinandan.one/email-model.md). `client.reply(email, ...)` derives threading and the `Re:` subject from the parent message server-side, you never set them yourself.

**Python**

```python
import primitive

client = primitive.client(api_key="prim_test")

def webhook_handler(body: bytes, headers: dict[str, str]) -> dict[str, object]:
    email = primitive.receive(
        body=body,
        headers=headers,
        secret="whsec_...",
    )

    client.reply(email, "Thank you for your email.")
    return {"ok": True}
```

**Go**

```go
package main

import (
	"context"
	"log"

	primitive "github.com/primitivedotdev/sdks/sdk-go"
)

func handle(ctx context.Context, body []byte, headers map[string]string) {
	email, err := primitive.Receive(primitive.HandleWebhookOptions{
		Body:    body,
		Headers: headers,
		Secret:  "whsec_...",
	})
	if err != nil {
		log.Printf("invalid webhook: %v", err)
		return
	}

	client, err := primitive.NewClient("prim_test")
	if err != nil {
		log.Fatal(err)
	}

	_, err = client.Reply(ctx, email, primitive.ReplyParams{BodyText: "Thank you for your email."})
	if err != nil {
		log.Printf("reply failed: %v", err)
	}
}
```

**CLI**

The CLI's task-oriented commands cover the same send/reply flow without any code:

```bash
primitive emails latest --limit 5
primitive reply --id <inbound-email-id> --body "Thanks!"
```

See [Sending, Replying, and Searching Email from the CLI](https://test.abhinandan.one/cli-overview/cli-email-commands.md) for the full command set.

**Expected result:** the handler returns `{"ok": true}` (Node) or its language equivalent, and the sender receives a reply threaded under the original message (same `References`, subject prefixed `Re:`).

### 4. Send a new email

Outbound mail that isn't a reply uses `send` instead. By default `send` returns as soon as Primitive accepts the message; pass `wait: true` to use [wait mode](https://test.abhinandan.one/email-model.md) and get the terminal SMTP delivery status before your handler returns.

**Choose one of the following:**

**Node.js**

```ts
import primitive from "@primitivedotdev/sdk";

const client = primitive.client({
  apiKey: process.env.PRIMITIVE_API_KEY!,
});

const result = await client.send({
  from: "Support <support@example.com>",
  to: "alice@example.com",
  subject: "Hello",
  bodyText: "Hi there",
  wait: true,
  waitTimeoutMs: 5000,
});

console.log(result.id, result.status, result.queueId, result.deliveryStatus);
```

**Python**

```python
import primitive

client = primitive.client(api_key="prim_test")

result = client.send(
    from_email="Support <support@example.com>",
    to="alice@example.com",
    subject="Hello",
    body_text="Hi there",
    wait=True,
    wait_timeout_ms=5000,
)

print(result.id, result.status, result.queue_id, result.delivery_status)
```

**Go**

```go
package main

import (
	"context"
	"log"
	"os"
	"time"

	primitive "github.com/primitivedotdev/sdks/sdk-go"
)

func main() {
client, err := primitive.NewClient(os.Getenv("PRIMITIVE_API_KEY"))
if err != nil {
	log.Fatal(err)
}

ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second)
defer cancel()
wait := true

result, err := client.Send(ctx, primitive.SendParams{
	From:          "Support <support@example.com>",
	To:            "alice@example.com",
	Subject:       "Hello",
	BodyText:      "Hi there",
	Wait:          &wait,
	WaitTimeoutMs: 5000,
})
if err != nil {
	log.Fatal(err)
}
log.Println(result.ID, result.Status, result.DeliveryStatus)
}
```

**CLI**

```bash
primitive send --to alice@example.com --body "Hello!" --wait
```

**Verify:** check `result.status` / `result.id` in the response, or run `primitive emails latest` (CLI) / your inbox provider's sent-mail view to confirm delivery.

## Escape hatches

> **Tip:** Framework has no standard `Request` object? Use the lower-level `receive({ body, headers, secret })` form (Node) or the equivalent `handle_webhook` (Python) / `primitive.Receive` (Go) call directly, see [Receiving Inbound Email](https://test.abhinandan.one/node-sdk-receiving-email.md).

> **Tip:** Need the full generated HTTP API (Memories, semantic search, account management)? Reach for the [generated API client](https://test.abhinandan.one/python-generated-api-client.md) instead of the high-level `send`/`reply`/`forward` surface.

> **Tip:** Already picked Python or Go for your stack? All three SDKs implement the identical inbound/outbound model, pick by language, not by capability gap. See [Python SDK Quickstart](https://test.abhinandan.one/python-sdk-quickstart.md) or [Go SDK Quickstart](https://test.abhinandan.one/go-sdk-quickstart.md) for the language-specific deep dive.
