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.tsimport 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.
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
- How Outpost Fits Together - the system map
- Messages - lifecycle, statuses, retries
- Webhooks - push notifications to your systems
- API Reference - every endpoint, schema, and error, generated from the OpenAPI document
- Generating a Typed Client - end-to-end type safety from the same spec