Skip to main content

Overview

Courier Inbox and Toast require a user-scoped JWT to authenticate requests from your frontend. Your backend must generate these tokens server-side using your Courier API key, then pass them securely to your client application. If you’re adding Inbox or Toast to an existing application, you can implement JWT generation in your backend with minimal changes to your existing authentication flow.
Never expose your Courier API key to client-side code. Always generate JWTs on your backend server using server-side code.

JWT Authentication Benefits

  • The token generated by your backend contains only the necessary permissions for Inbox and Toast to function, which gives you fine-grained control over user access.
  • Because tokens are scoped to specific users, each user can only access their own messages and preferences.
  • Tokens expire after a set duration, limiting the impact of potential security leaks.
  • Your Courier API key stays secure on your backend and is never exposed to client-side code.

Required Permissions

When generating a JWT, you must include these scopes:
  • user_id:{{userId}} - Grants access to a specific user
  • inbox:read:messages - Allows reading inbox messages
  • inbox:write:events - Enables marking messages as read, archived, etc.
  • read:preferences - Allows reading user notification preferences
  • write:preferences - Enables updating user preferences
  • read:brands - Grants access to brand settings
Optional scopes:
  • read:user-tokens - For managing push tokens
  • write:user-tokens - For registering device tokens

Implement JWT Generation

Follow these steps to add JWT generation to your backend:
1

Install dependencies

Install the required packages for your backend framework.
npm install express cors dotenv
2

Store your Courier API key

Store your Courier API key as an environment variable (never hardcode it in your source code). You can find your keys in Courier settings.
# .env
COURIER_API_KEY=your_courier_api_key_here
3

Create the JWT endpoint

Create a POST endpoint that accepts a user ID and calls Courier’s token generation API.
const express = require('express');
const cors = require('cors');
require('dotenv').config();

const app = express();
const PORT = 3000;

// Middleware
app.use(cors());
app.use(express.json());

// JWT Generation endpoint
app.post('/api/generate-courier-jwt', async (req, res) => {
  try {
    const { userId } = req.body;

    // Call Courier API directly to generate JWT
    const response = await fetch('https://api.courier.com/auth/issue-token', {
      method: 'POST',
      headers: {
        'Authorization': `Bearer ${process.env.COURIER_API_KEY}`,
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({
        scope: `user_id:${userId} inbox:read:messages inbox:write:events read:preferences write:preferences read:brands`,
        expires_in: '1d'
      })
    });

    if (!response.ok) {
      const errorData = await response.json().catch(() => ({}));
      throw new Error(`Courier API error: ${response.status} ${response.statusText}`);
    }

    const data = await response.json();

    res.json({ 
      token: data.token,
      expiresIn: '1d'
    });
  } catch (error) {
    console.error('Error generating JWT:', error);
    res.status(500).json({ 
      error: 'Failed to generate JWT',
      message: error.message 
    });
  }
});
The endpoint validates the user ID, calls Courier’s /auth/issue-token endpoint with the required scopes, and returns the JWT token. Token expiration is set to 1 day (expires_in: '1d').
4

Add user validation (recommended)

Add validation to ensure the requesting user is authenticated and authorized.
// Example: Validate user session before generating JWT (Node.js/Express)
app.post('/api/generate-courier-jwt', async (req, res) => {
  try {
    // Get user ID from session/auth system
    const authenticatedUserId = req.session.userId; // or use your authentication method
    const { userId } = req.body;

    // Ensure requested userId matches authenticated user
    if (!authenticatedUserId || authenticatedUserId !== userId) {
      return res.status(401).json({
        error: 'Unauthorized'
      });
    }
    // Continue with JWT generation...
    // (call /auth/issue-token, etc.)
  } catch (error) {
    // Proper error handling
    res.status(500).json({ error: 'Server error' });
  }
});
It’s best practice to verify that the user you’re issuing a JWT for is the same user as the logged-in session or authenticated identity in your application, according to your security requirements.
5

Start your server

Start your backend server and verify the endpoint is accessible.Node.js:
node server.js
Python:
python app.py

Pass the Token to Your Frontend

After your backend generates the JWT, you will be able to retrieve it to enable Courier Inbox or Toast features in your front-end project.

1. Call Your Backend Endpoint

const generateCourierJwt = async (userId) => {
  try {
    const response = await fetch(`/api/generate-courier-jwt`, {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({ userId }),
      credentials: 'include', // Include cookies if using session auth
    });
    
    if (!response.ok) {
      throw new Error('Failed to generate JWT');
    }
    
    const { token } = await response.json();
    return token;
  } catch (error) {
    console.error('Error fetching JWT:', error);
    throw error;
  }
};

2. Authenticate Courier SDK

Pass the token to Courier’s authentication method:
import { useCourier } from '@trycourier/courier-react';

function App() {
  const [courierJwt, setCourierJwt] = useState();
  const courier = useCourier();

  useEffect(() => {
    const fetchAndAuthenticate = async () => {
      const userId = getCurrentUserId(); // Your auth logic
      const token = await generateCourierJwt(userId);
      
      courier.shared.signIn({ 
        userId, 
        jwt: token 
      });
      
      setCourierJwt(token);
    };

    fetchAndAuthenticate();
  }, []);

  return <CourierInbox />;
}
Best Practice: Generate a new JWT when the user logs in, and refresh it before expiration if your token lifetime exceeds the user session duration.

Security Best Practices

Token Expiration

Set appropriate expiration times based on your security requirements:
  • Short sessions: '1h' or '30m' for high-security applications
  • Standard sessions: '1d' for typical web applications
  • Long sessions: '7d' or '30d' only if you have robust token refresh mechanisms
Avoid extremely long expiration times. Tokens should expire when user sessions end or shortly after.

API Key Protection

  • Store your Courier API key in environment variables, never in source code
  • Use different API keys for development, staging, and production
  • Rotate API keys periodically
  • Restrict API key permissions in the Courier dashboard when possible

HTTPS Only

Always use HTTPS in production to encrypt JWT tokens in transit. Never send tokens over unencrypted HTTP connections.

Debugging Tips

  1. Log token generation: Log successful token generation (but never log the full token value)
  2. Check scopes: Verify the token includes all required scopes for your use case
  3. Monitor expiration: Track when tokens expire to identify refresh issues
  4. Test locally: Use Courier’s test API key to verify endpoint behavior before deploying
Learn More: For complete API documentation, see the Auth API Reference and Issue Token endpoint.