Skip to main content

Overview

Autumn’s backend integration creates API routes that proxy requests to the Autumn API while injecting customer identity from your auth system. This ensures customers can only access their own data.

Framework Adapters

Next.js

For Next.js App Router, use the autumn-js/next adapter:
// app/api/autumn/[...autumn]/route.ts
import { autumnHandler } from 'autumn-js/next';
import { cookies } from 'next/headers';
import { validateSession } from '@/lib/auth';

const handler = autumnHandler({
  secretKey: process.env.AUTUMN_SECRET_KEY,
  identify: async (request) => {
    // Get session from cookies
    const sessionCookie = cookies().get('session');
    if (!sessionCookie) {
      return null; // No authenticated user
    }
    
    // Validate and get user ID from your auth system
    const session = await validateSession(sessionCookie.value);
    if (!session) return null;
    
    return {
      customerId: session.userId,
      customerData: {
        email: session.email,
        name: session.name,
      },
    };
  },
});

export { handler as GET, handler as POST, handler as DELETE };

Handler Options

identify
(request: Request) => AuthResult
required
Function to extract customer identity from the request. Must return an object with customerId or null for unauthenticated requests.
secretKey
string
Autumn API secret key. Defaults to AUTUMN_SECRET_KEY environment variable.
autumnURL
string
Autumn API base URL. Defaults to https://api.useautumn.com/v1.
pathPrefix
string
default:"/api/autumn"
Path prefix for routes. Must match the route file location.

Hono

For Hono applications, use the autumn-js/hono adapter:
import { Hono } from 'hono';
import { autumnHandler } from 'autumn-js/hono';

const app = new Hono();

app.use('/api/autumn/*', autumnHandler({
  secretKey: process.env.AUTUMN_SECRET_KEY,
  identify: async (c) => {
    // Get user from Hono context (set by your auth middleware)
    const user = c.get('user');
    if (!user) return null;
    
    return {
      customerId: user.id,
      customerData: {
        email: user.email,
        name: user.name,
      },
    };
  },
}));

export default app;

Handler Options

identify
(c: Context) => AuthResult
required
Function to extract customer identity from the Hono context.
secretKey
string
Autumn API secret key.
autumnURL
string
Autumn API base URL.
pathPrefix
string
default:"/api/autumn"
Path prefix for routes.

Framework-Agnostic

For custom frameworks, use the generic autumnHandler:
import { autumnHandler } from 'autumn-js/backend';

const result = await autumnHandler({
  request: {
    url: '/api/autumn/getOrCreateCustomer',
    method: 'POST',
    body: {},
  },
  customerId: 'user_123',
  customerData: {
    email: '[email protected]',
    name: 'John Doe',
  },
  clientOptions: {
    secretKey: process.env.AUTUMN_SECRET_KEY,
  },
});

console.log(result.statusCode); // 200
console.log(result.response);   // { id: 'user_123', ... }

Handler Options

request
object
required
Request object with url, method, and body.
customerId
string
Customer ID to inject into the request.
customerData
CustomerData
Additional customer data (email, name, etc.).
clientOptions
object
Client configuration with secretKey and optional baseURL.
pathPrefix
string
default:"/api/autumn"
Path prefix for routes.
routes
RouteDefinition[]
Custom route definitions (for advanced use cases).

Identity Resolution

The identify function is called for every request and must return customer identity:

Return Type

type AuthResult = {
  customerId: string | null | undefined;
  customerData?: {
    email?: string;
    name?: string;
    // ... other customer fields
  };
} | null;

Examples

Basic User ID

identify: async (request) => {
  const userId = await getUserIdFromRequest(request);
  if (!userId) return null;
  
  return { customerId: userId };
}

With Customer Data

identify: async (request) => {
  const user = await getUserFromRequest(request);
  if (!user) return null;
  
  return {
    customerId: user.id,
    customerData: {
      email: user.email,
      name: user.name,
    },
  };
}

Organization-Based

identify: async (request) => {
  const session = await getSession(request);
  if (!session) return null;
  
  // Use organization ID as customer ID
  const orgId = session.activeOrganizationId;
  if (!orgId) return null;
  
  return {
    customerId: orgId,
    customerData: {
      name: session.organizationName,
    },
  };
}

User + Organization Composite

identify: async (request) => {
  const session = await getSession(request);
  if (!session) return null;
  
  // Composite customer ID
  const customerId = session.activeOrganizationId 
    ? `org_${session.activeOrganizationId}`
    : `user_${session.userId}`;
  
  return { customerId };
}

Better Auth Plugin

If you’re using Better Auth, the Autumn plugin simplifies setup:
import { betterAuth } from 'better-auth';
import { autumn } from 'autumn-js/better-auth';

export const auth = betterAuth({
  database: { /* ... */ },
  plugins: [
    autumn({
      secretKey: process.env.AUTUMN_SECRET_KEY,
      customerScope: 'user', // or 'organization' | 'user_and_organization'
    }),
  ],
});

Customer Scope

customerScope
'user' | 'organization' | 'user_and_organization'
default:"user"
Determines how customer identity is resolved:
  • user - Customer ID is the user’s ID
  • organization - Customer ID is the active organization’s ID
  • user_and_organization - Customer ID combines both (format: user_{userId}_org_{orgId})

Custom Identity Function

Override the default behavior with a custom identify function:
autumn({
  secretKey: process.env.AUTUMN_SECRET_KEY,
  identify: ({ session, organization }) => {
    if (!session) return null;
    
    // Custom logic
    if (organization) {
      return {
        customerId: organization.id,
        customerData: {
          name: organization.name,
        },
      };
    }
    
    return {
      customerId: session.user.id,
      customerData: {
        email: session.user.email,
        name: session.user.name,
      },
    };
  },
})

Identify Function Parameters

session
BetterAuthSession | null
Better Auth session object with user and session data.
organization
BetterAuthOrganization | null
Active organization object (if using Better Auth organizations).

Advanced: Custom Routes

For advanced use cases, define custom routes:
import { autumnHandler, buildRouter, routeConfigs } from 'autumn-js/backend';

const customRoutes = buildRouter([
  routeConfigs.getOrCreateCustomer,
  routeConfigs.attach,
  routeConfigs.listPlans,
  // Add only the routes you need
]);

const handler = autumnHandler({
  identify: async (request) => { /* ... */ },
  routes: customRoutes,
});

Available Route Configs

  • getOrCreateCustomer
  • attach
  • previewAttach
  • updateSubscription
  • previewUpdateSubscription
  • multiAttach
  • previewMultiAttach
  • setupPayment
  • openCustomerPortal
  • createReferralCode
  • redeemReferralCode
  • listPlans
  • listEvents
  • aggregateEvents

Core Handler

For maximum flexibility, use the core handler directly:
import { createCoreHandler } from 'autumn-js/backend';

const handler = createCoreHandler({
  identify: async (raw) => {
    // `raw` is the original request object from your framework
    const userId = await extractUserId(raw);
    return { customerId: userId };
  },
  secretKey: process.env.AUTUMN_SECRET_KEY,
});

const result = await handler({
  method: 'POST',
  path: '/api/autumn/attach',
  body: { planId: 'plan_pro' },
  raw: originalRequest,
});

console.log(result.status); // 200
console.log(result.body);   // Response data

Core Handler Options

identify
(raw: unknown) => AuthResult
required
Function to extract customer identity. Receives the raw request object.
secretKey
string
Autumn API secret key.
autumnURL
string
Autumn API base URL.
pathPrefix
string
default:"/api/autumn"
Path prefix for routes.
routes
RouteDefinition[]
Custom route definitions.

Core Handler Request

method
string
required
HTTP method (GET, POST, DELETE).
path
string
required
Request path (e.g., /api/autumn/attach).
body
unknown
Request body (parsed JSON).
raw
unknown
Original framework request object (passed to identify).

Error Handling

The handler automatically transforms errors into appropriate HTTP responses:
// Authentication error
{ status: 401, body: { message: 'Unauthorized', code: 'unauthorized' } }

// Validation error
{ status: 400, body: { message: 'Invalid plan ID', code: 'invalid_request' } }

// Not found
{ status: 404, body: { message: 'Customer not found', code: 'not_found' } }

// Internal error
{ status: 500, body: { message: 'Internal server error', code: 'internal_error' } }

Utilities

secretKeyCheck

Validate that the secret key is configured:
import { secretKeyCheck } from 'autumn-js/backend';

const key = secretKeyCheck(process.env.AUTUMN_SECRET_KEY);
// Throws if key is missing or invalid

sanitizeBody

Sanitize request body before sending to Autumn API:
import { sanitizeBody } from 'autumn-js/backend';

const cleaned = sanitizeBody(requestBody, ['customerId', 'customerData']);
// Removes protected fields that should be injected by backend

backendSuccess / backendError

Create standardized backend responses:
import { backendSuccess, backendError } from 'autumn-js/backend';

const successResponse = backendSuccess({ data: { id: '123' } });
// { success: true, data: { id: '123' } }

const errorResponse = backendError({
  message: 'Invalid request',
  code: 'invalid_request',
  statusCode: 400,
});
// { success: false, error: { message: '...', code: '...' } }

Type Exports

All backend types are exported for TypeScript users:
import type {
  AuthResult,
  CustomerId,
  CustomerData,
  ResolvedIdentity,
  CoreHandlerOptions,
  UnifiedRequest,
  UnifiedResponse,
  RouteDefinition,
  BackendResult,
  BackendErrorBody,
} from 'autumn-js/backend';

Build docs developers (and LLMs) love