← Back to Papier

BUILT FOR YOUR CODEBASE

Your templates.
Your types.
Your delivery stack.

Keep presentation in Papier and application logic in your app. Bring them together with a generated TypeScript client.

Create your workspace
TypeScript SDKSchema validationVersioned templatesYour email provider
01

SET UP ONCE

Connect your project.

Create an email template in your workspace, give it a data schema, and publish it. Then install the SDK and connect your local project.

TerminalCLI
npm install @usepapier/client @usepapier/cli

npx @usepapier/cli login
npx @usepapier/cli init
npx @usepapier/cli sync
login

Authenticate in your browser.

init

Select your organization and projects.

sync

Resolve versions and generate types.

02

A SMALL INTEGRATION

Render with real data.

The generated createPapier factory knows your projects, template names, and data shapes. Render HTML, then pass it to your existing delivery provider.

Example: a project named acme and a published welcome-email template with a name string in its schema. Replace these with your own.

welcome.tsTypeScript
import { createPapier } from '@usepapier/client';

const papier = createPapier({
  project: 'acme',
  apiKey: process.env.PAPIER_API_KEY,
  environment: 'production',
});

const html = await papier.emails.welcomeEmail.renderHTML({
  data: { name: 'Alex' },
});

// Send the rendered HTML with your existing email provider.
await yourEmailProvider.send({
  to: 'alex@example.com',
  subject: 'Welcome aboard, Alex',
  html,
});

Your provider stays yours. Rendering needs no email adapter. To use the SDK’s send() methods, configure an email adapter in emailConfig.adapters.

Working with an outbox?

Use build() to validate and create a serializable payload for later sending. It returns the template reference, recipient, and data; use renderHTML() when you need the rendered HTML.

outbox.tsTypeScript
// Validate now. Store the payload for later.
const payload = await papier.emails.welcomeEmail.build({
  to: 'alex@example.com',
  data: { name: 'Alex' },
});

await yourOutbox.insert(payload);

// In a worker, with an email adapter configured:
// await papier.send(payload);

yourEmailProvider and yourOutbox represent your own application services.

03

CHANGE WITH INTENTION

Keep versions explicit.

Choose a template version range in .papier/papier.json. Sync to resolve versions, update the lockfile, and regenerate your client.

.papier/papier.json · excerptJSON
{
  "organization": "your-team",
  "projects": {
    "acme": {
      "templates": {
        "welcome-email": "^1.0.0"
      }
    }
  }
}
↻

Sync when templates change

Run npx @usepapier/cli sync to fetch schemas and refresh the generated client.

⌘

Generate from the lockfile

Run npx @usepapier/cli generate to rebuild the client from an existing lockfile.

Use the dashboard to publish template versions to your project’s environments. Keep .papier/papier.json and .papier/papier.lock.json in version control so the integration is reviewable with your code.

FROM DESIGN TO YOUR APP

Make room for
better messages.

Build the template together.
Ship it with a typed contract.

Start with Papier