---
title: "SDKs"
description: "Official Primitive SDKs for TypeScript (Node.js), Python, and Go, including install commands and high-level send/receive helpers."
canonical: "https://docs.primitive.dev/docs/sdks"
last-updated: "2026-05-17T00:00:00.000Z"
---

# Primitive SDKs (TypeScript, Python, Go)

Primitive publishes official SDKs for Node.js, Python, and Go. Use SDKs in application code and Functions. Use the CLI for terminal workflows; install it with Homebrew from [primitivedotdev/tap](https://github.com/primitivedotdev/homebrew-tap) or with npm as `primitive` (also published under the scoped name `@primitivedotdev/cli`).

LanguagePackageRuntimeRegistry and API referenceTypeScript (Node.js)`@primitivedotdev/sdk`Node.js 22 or newer[npm](https://www.npmjs.com/package/@primitivedotdev/sdk)Python`primitivedotdev`Python 3.10 or newer[PyPI](https://pypi.org/project/primitivedotdev/)Go`github.com/primitivedotdev/sdks/sdk-go`Go 1.25 or newer[pkg.go.dev](https://pkg.go.dev/github.com/primitivedotdev/sdks/sdk-go)

All three SDKs are open source in [github.com/primitivedotdev/sdks](https://github.com/primitivedotdev/sdks) and generated from the same OpenAPI spec.

## Install


```
npm install @primitivedotdev/sdk
pnpm add @primitivedotdev/sdk
yarn add @primitivedotdev/sdk
bun add @primitivedotdev/sdk
```

```
pip install primitivedotdev
uv add primitivedotdev
poetry add primitivedotdev
```

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


## Receive an Email


```
import primitive from '@primitivedotdev/sdk';


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, {
    text: 'Got it.',
  });


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

```
import os
import primitive
from fastapi import FastAPI, Request


app = FastAPI()
client = primitive.client(api_key=os.environ['PRIMITIVE_API_KEY'])


@app.post('/webhooks/email')
async def handle(request: Request):
    body = await request.body()
    email = primitive.receive(
        body=body,
        headers=dict(request.headers),
        secret=os.environ['PRIMITIVE_WEBHOOK_SECRET'],
    )


    client.reply(email, {'text': 'Got it.'})
    return {'ok': True}
```

```
func handle(w http.ResponseWriter, r *http.Request) {
    body, _ := io.ReadAll(r.Body)
    email, err := primitive.Receive(primitive.HandleWebhookOptions{
        Body:    body,
        Headers: r.Header,
        Secret:  os.Getenv("PRIMITIVE_WEBHOOK_SECRET"),
    })
    if err != nil {
        http.Error(w, "invalid signature", http.StatusForbidden)
        return
    }


    client, _ := primitive.NewClient(os.Getenv("PRIMITIVE_API_KEY"))
    client.Reply(context.Background(), email, primitive.ReplyParams{
        BodyText: "Got it.",
    })


    w.WriteHeader(http.StatusOK)
}
```


Reply helpers use reply-specific body keys: `text` and `html` in Node.js and Python, and `BodyText` and `BodyHTML` in Go. Send helpers use send-specific names such as `bodyText` in TypeScript.

## Send an Email


```
const result = await client.send({
  from: 'support@yourdomain.com',
  to: 'alice@example.com',
  subject: 'Order confirmed',
  bodyText: 'Your order is on its way.',
  wait: true,
});


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

```
result = client.send(
    from_email='support@yourdomain.com',
    to='alice@example.com',
    subject='Order confirmed',
    body_text='Your order is on its way.',
    wait=True,
)


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

```
wait := true
result, err := client.Send(context.Background(), primitive.SendParams{
    From:     "support@yourdomain.com",
    To:       "alice@example.com",
    Subject:  "Order confirmed",
    BodyText: "Your order is on its way.",
    Wait:     &wait,
})
```


## High-Level Helpers

The SDKs provide high-level helpers for:

- verifying and parsing inbound webhooks;

- sending new outbound messages;

- replying to an inbound email with threading derived from the parent;

- forwarding inbound context to a new recipient;

- deciding whether to trust an inbound email (`validateEmailAuth` for an overall SPF/DKIM/DMARC verdict, `isTrustedSender` to anchor that verdict to an expected From domain);

- verifying webhook signatures manually when you need lower-level control.

## Functions Runtime Caveat

Hosted Primitive Functions execute JavaScript. Function handler examples are therefore TypeScript/JavaScript-only. Application code outside hosted Functions can use Node.js, Python, or Go SDKs.

## Low-Level API Client

Inside hosted Functions, import the API-safe subpath:


```
import { createPrimitiveClient } from '@primitivedotdev/sdk/api';
```


The `/api` subpath avoids `node:crypto` (which the root and `/webhook` entries pull in), and its file attachment and payload helpers load `node:fs` lazily at call time instead of importing it, so Workers-style runtimes can bundle it without Node compatibility.

## Primitive Memories

Primitive Memories are durable JSON key-value records. In Node.js, use
`createPrimitiveClient().memories`; org scope is the default, and function scope
uses the function id UUID.


```
import { createPrimitiveClient } from '@primitivedotdev/sdk/api';


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


await client.memories.set({
  key: 'thread:latest',
  value: { email_id: 'em_123' },
});


const memory = await client.memories.get('thread:latest');


const page = await client.memories.search({
  prefix: 'thread:',
  includeValue: false,
});


await client.memories.delete('thread:latest');
```


Pass `scope: { type: 'function', id: '<function-id>' }` when setting a
function-scoped memory. For get, delete, and search, pass
`scope_type=function&scope_id=<function-id>`. Values must serialize as JSON.
`client.memories.search` is key prefix search, not free-text or semantic
search; use `client.semanticSearch(...)` for mail search.

See [Primitive Memories](/docs/memories) for TTLs, version checks, metadata,
search, and billing behavior.

## x402 Payments

All three SDKs ship an x402 client for collecting and paying USDC non-custodially. Import the x402 client (in Node, `@primitivedotdev/sdk/x402`; in Python, `primitive.x402`; in Go, the `primitive` package's `NewX402Client`), register a payout address, create a challenge, and settle it with a locally-held key. See [Collecting Payments](/docs/collecting-payments) for the full flow.

See [CLI](/docs/cli) for terminal workflows.

Maintained by the [primitive.dev team](/about) · [Trust & security](/trust)Last updated: May 17, 2026
