Skip to content

Security and API keys

Permission Can do Use for
full_access Every endpoint Your own tooling, CLI, MCP server, admin scripts
sending_access Only POST /emails and POST /emails/batch Application servers that just send mail

Calling any other endpoint with a sending_access key returns 401 restricted_api_key (“This API key is restricted to only send emails”).

A sending_access key can additionally be bound to one domain with domain_id. It can then only send from addresses on that domain; any other from returns 401 restricted_api_key. Use it to give each product or client its own key.

api-keys.ts
import { SpacePost } from "@spacepost/node";
const spacepost = new SpacePost();
// A key that can only send, and only from one domain.
const { data: key, error } = await spacepost.apiKeys.create({
name: "worker-prod",
permission: "sending_access",
domainId: "0192e1c4-0000-7000-8000-000000000001",
});
if (error) throw new Error(error.message);
console.log(key.token); // sp_live_..., shown only once
const { data: keys } = await spacepost.apiKeys.list();
console.log(keys?.data.map((k) => `${k.name} ${k.prefix}... ${k.permission}`));
await spacepost.apiKeys.remove(key.id);

name is 1 to 50 characters. domain_id is only allowed with sending_access.

  • Keys look like sp_live_ followed by 32 random base62 characters.
  • SpacePost stores only a SHA-256 hash. The full key is shown once, at creation. If you lose it, create a new key.
  • The dashboard and GET /api-keys show the prefix (sp_live_ plus 4 characters), the permission, the domain and last_used_at.
  • Deleting a key (DELETE /api-keys/{id}) revokes it immediately.
  • Never put keys in client-side code, mobile apps or public repositories. The API allows cross-origin requests only so API playgrounds work; send email from your server.
  • Use one key per service and environment, named clearly (worker-prod, ci-staging).
  • Prefer sending_access, restricted to a domain.
  • Rotate by creating a new key, deploying it, checking last_used_at of the old one, then deleting the old one.
  • Keep keys in a secret manager or environment variables, not in code.
  • Revoke immediately if a key leaks. If it was committed to GitHub, assume it is compromised.
  • SMTP uses the same key as the password. See SMTP relay.

All traffic uses TLS. The API is served at https://api.spacepost.dev, and the SMTP relay at smtp.spacepost.dev:25976 requires STARTTLS before authentication (implicit TLS on port 465 is not offered yet).

Other protections: failed SMTP authentication attempts are throttled per IP; webhook endpoints and attachment URLs must resolve to public addresses (private networks are refused); the unsubscribe links are signed per contact.

SpacePost is operated by Studio 404 d.o.o., an EU company in Slovenia, and keeps customer data in the EU:

  • Sending: Amazon SES in AWS eu-central-1 (Frankfurt, Germany). Every domain sends from this region.
  • Application and database: the API, dashboard, workers and Postgres database run on Railway in its EU region (Amsterdam, Netherlands).
  • Isolation: each team sends through its own SES tenant, so one customer’s reputation problems don’t affect others.
  • Website and docs are static pages served by Cloudflare’s global network; no customer data is stored there.

Some providers (AWS, Stripe, Railway) are US companies. They are listed with what they process and where on the sub-processor list. A GDPR data processing addendum is part of the Terms of Service, which are governed by Slovenian law.

Email support@spacepost.dev with details. Please do not test against other customers’ data.