Outpost logoOutpost Docs

Getting Started

Send your first SMS through Outpost.

This guide walks through the minimum path from zero to a delivered SMS: a tenant, a project, an API key, and a send.

Tenant and project management endpoints require an admin API key. If you are integrating a downstream service, an Outpost administrator will hand you a tenant-scoped key and you can skip to step 4.

Prefer TypeScript? Generate types once, then use the same typed client for every step. For the full setup, see Generating a Typed Client.

npm install openapi-fetch
npm install -D openapi-typescript typescript
npx openapi-typescript "$OUTPOST_URL/v1/openapi.json" -o ./src/outpost-api.ts
import createClient from 'openapi-fetch';
import type { paths } from './outpost-api';

export const api = createClient<paths>({
  baseUrl: process.env.OUTPOST_URL!,
  headers: { 'x-api-key': process.env.OUTPOST_API_KEY! },
});

Use an admin key for steps 1-3, then switch OUTPOST_API_KEY (or create a second client) to the project key you create in step 3.

Create a tenant. A tenant represents your organization and scopes all of your data.

const { data, error } = await api.POST('/v1/tenants', {
  body: { name: 'Acme Corp' },
});

if (error) throw error;
const tenantId = data.tenantId;

Create a project under the tenant. Projects hold API key and webhook configuration. A tenant needs at least one project before it can send.

const { data, error } = await api.POST('/v1/tenants/{tenantId}/projects', {
  params: { path: { tenantId } },
  body: { name: 'Notifications Service' },
});

if (error) throw error;
const projectId = data.projectId;

Create an API key for the project. The plaintext key is returned exactly once as plainApiKey - store it securely. Outpost only keeps a SHA-256 hash.

const { data, error } = await api.POST('/v1/projects/{projectId}/api-keys', {
  params: { path: { projectId } },
  body: { name: 'production' },
});

if (error) throw error;
// Shown once. Persist this; you cannot retrieve it again.
const tenantKey = data.plainApiKey;

Send a message. Sends are asynchronous - the endpoint validates each recipient, queues accepted items, and returns 202 with per-item outcomes.

const sendApi = createClient<paths>({
  baseUrl: process.env.OUTPOST_URL!,
  headers: { 'x-api-key': tenantKey },
});

const { data, error } = await sendApi.POST('/v1/messages/sms', {
  body: {
    items: [{ to: '+15551234567', body: 'Hello from Outpost!' }],
    communicationId: 'welcome-blast-1',
  },
});

if (error) {
  console.error('Send failed:', error);
} else {
  console.log('Accepted:', data.accepted, 'Rejected:', data.rejected);
}

The optional communicationId groups the batch and enables deduplication - the same phone can never receive two messages with the same communicationId.

Track the outcome. Delivery happens asynchronously via the carrier network. You can poll the message, browse its events, or - the recommended approach - configure a webhook on your project so Outpost pushes DELIVERED, FAILED, and opt-out notifications to you.

What happens to your message

After the API accepts an item, the message flows through validation, a send queue, AWS End User Messaging, and delivery receipt processing. The full lifecycle - statuses, retries, and compliance footers - is documented in Messages.

Where to go next

On this page