@lalternative/spore-sdk
v0.3.0
Published
Typed TypeScript client for the Spore API, generated from OpenAPI 3.1.
Readme
@lalternative/spore-sdk
Typed TypeScript client for the Spore API, generated from
apps/core/docs/swagger.yaml with
orval.
Install
Published to the public npmjs.org registry under the @lalternative
scope. Install normally — no registry override or token needed:
pnpm add @lalternative/spore-sdkInside the Spore monorepo, depend on the workspace version directly:
// apps/web/package.json
{
"dependencies": {
"@lalternative/spore-sdk": "workspace:*"
}
}Quickstart — from zero to your first email
You need three things before sendEmail will accept a message:
- an API key (
sk_live_…) - an identity (your sending domain), in
verifiedstate - an allowed From address registered on that identity
1. Create an API key
Sign in at app.sporee.fr, open API keys,
click Create. The plaintext sk_live_… is shown once — copy it
into your env:
# .env in your service
SPORE_API_KEY=sk_live_xxxxxxxxxxxxxxxxxxxxxxxx2. Configure the client
Call configureSporeClient once at boot. After that every generated
function uses the shared axios instance.
import { configureSporeClient, getSporeAPI } from "@lalternative/spore-sdk";
configureSporeClient({
apiKey: process.env.SPORE_API_KEY!,
// baseURL defaults to https://api.sporee.fr — override for staging
// or local dev (e.g. http://localhost:4110).
});
const api = getSporeAPI();3. Register and verify a domain
const created = await api.createIdentity({ name: "example.com" });
// → publish created.records (DKIM, SPF, DMARC, bounce CNAME) on your DNS
const verified = await api.verifyIdentity(created.domainId);
// verified.status === "verified" once DKIM + SPF resolve correctlyYou can also do this from the webapp under Identities → Add domain; it gives you the DNS records to copy into your registrar.
4. Add an allowed sending address
POST /emails rejects any from address that is not on the identity's
active allowlist. Register the local-parts you intend to send from:
await api.addIdentityAddress(created.domainId, {
localPart: "hello",
label: "Marketing",
});Other operations on the allowlist:
await api.disableIdentityAddress(created.domainId, "hello", { reason: "rotated" });
await api.removeIdentityAddress(created.domainId, "hello");You can also manage the allowlist from the webapp at /identities/:id,
section Allowed sending addresses.
5. Send
await api.sendEmail(
{
identityId: created.domainId,
from: "[email protected]", // must match an active allowlist entry
to: ["[email protected]"],
subject: "Hello",
html: "<p>Hi!</p>",
},
{ headers: { "Idempotency-Key": crypto.randomUUID() } },
);Including an Idempotency-Key lets you safely retry the call: a replay
within 24 h returns the original 2xx response without re-sending.
Reusing the key with a different body returns 422.
Configuration reference
configureSporeClient(opts) accepts:
| Option | Required | Default | Notes |
|-----------|----------|----------------------------|-------|
| apiKey | yes* | — | Sent as Authorization: Bearer <apiKey>. Accepts an sk_live_… managed key (recommended), an HS256 JWT signed with JWT_SECRET, or the static API_KEY value (dev only). |
| baseURL | no | https://api.sporee.fr | Override for staging / local dev. |
| axios | no | — | Inject your own AxiosInstance (interceptors, retry, telemetry). When set, apiKey and baseURL are ignored — wire them into your instance directly. |
* You can omit apiKey only when you also pass a custom axios
instance that handles auth itself.
Auth modes accepted by the server
The server reads Authorization: Bearer <token> and tries, in order:
- Three-segment string → treated as a JWT, validated against
JWT_SECRET. Thesubclaim becomestenant_id. - Anything else →
- if it starts with
sk_live_, looked up against the managed-keys table (bcrypt-hashed at rest, revocation is immediate), - otherwise compared with the static
API_KEYenv (tenant fixed to"default", dev only).
- if it starts with
Regenerate
The generated code lives under src/generated/. Regenerate after every
change to apps/core/docs/swagger.yaml:
pnpm --filter @lalternative/spore-sdk generate
# or, including the swag regeneration upstream:
sklp run generateBuild
pnpm --filter @lalternative/spore-sdk build