> ## Documentation Index
> Fetch the complete documentation index at: https://www.courier.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# How to Send a JWT from Your Backend

> Configure your backend to generate user-scoped JWTs for Courier Inbox and Toast, enabling secure client-side access to messages, preferences, and events.

## 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.

<Warning>
  **Never expose your Courier API key** to client-side code. Always generate JWTs on your backend server using server-side code.
</Warning>

## 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:

<Steps>
  <Step title="Install dependencies">
    Install the required packages for your backend framework.

    <CodeGroup>
      ```bash Node.js theme={null}
      npm install express cors dotenv
      ```

      ```bash Python theme={null}
      pip install flask flask-cors python-dotenv requests
      ```

      ```bash Java (Maven) theme={null}
      # Add to pom.xml dependencies section
      <dependency>
          <groupId>org.springframework.boot</groupId>
          <artifactId>spring-boot-starter-web</artifactId>
      </dependency>
      ```

      ```bash PHP (Laravel) theme={null}
      # Laravel's HTTP client is built-in (uses Guzzle)
      # No additional packages required for basic usage
      ```

      ```bash Ruby (Rails) theme={null}
      # Add to Gemfile:
      gem 'httparty'

      # Then run:
      bundle install
      ```
    </CodeGroup>
  </Step>

  <Step title="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](https://app.courier.com/settings/api-keys).

    ```bash theme={null}
    # .env
    COURIER_API_KEY=your_courier_api_key_here
    ```
  </Step>

  <Step title="Create the JWT endpoint">
    Create a POST endpoint that accepts a user ID and calls Courier's [token generation API](/api-reference/authentication/create-a-jwt).

    <CodeGroup>
      ```javascript Node.js theme={null}
      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 
          });
        }
      });
      ```

      ```python Python theme={null}
      from flask import Flask, request, jsonify
      from flask_cors import CORS
      import os
      import requests
      from dotenv import load_dotenv

      load_dotenv()

      app = Flask(__name__)
      CORS(app)

      @app.route('/api/generate-courier-jwt', methods=['POST'])
      def generate_courier_jwt():
          try:
              data = request.get_json()
              user_id = data.get('userId')
              
              # Call Courier API to generate JWT
              courier_api_key = os.getenv('COURIER_API_KEY')
              courier_url = 'https://api.courier.com/auth/issue-token'
              
              headers = {
                  'Authorization': f'Bearer {courier_api_key}',
                  'Content-Type': 'application/json'
              }
              
              payload = {
                  'scope': f'user_id:{user_id} inbox:read:messages inbox:write:events read:preferences write:preferences read:brands',
                  'expires_in': '1d'
              }
              
              response = requests.post(courier_url, headers=headers, json=payload)
              
              if not response.ok:
                  error_msg = f'Courier API error: {response.status_code} {response.reason}'
                  return jsonify({'error': error_msg}), 500
              
              courier_data = response.json()
              
              return jsonify({
                  'token': courier_data['token'],
                  'expiresIn': '1d'
              })
          
          except Exception as e:
              print(f'Error generating JWT: {str(e)}')
              return jsonify({
                  'error': 'Failed to generate JWT',
                  'message': str(e)
              }), 500

      if __name__ == '__main__':
          app.run(port=3000)
      ```

      ```java Spring Boot theme={null}
      package com.example.controller;

      import org.springframework.beans.factory.annotation.Value;
      import org.springframework.http.*;
      import org.springframework.web.bind.annotation.*;
      import org.springframework.web.client.RestTemplate;

      import java.util.HashMap;
      import java.util.Map;

      @RestController
      @RequestMapping("/api")
      public class JwtController {
          
          @Value("${courier.api.key}")
          private String courierApiKey;
          
          private final RestTemplate restTemplate = new RestTemplate();
          
          @PostMapping("/generate-courier-jwt")
          public ResponseEntity<?> generateCourierJwt(@RequestBody Map<String, String> request) {
              try {
                  String userId = request.get("userId");
                  
                  // Call Courier API to generate JWT
                  String courierUrl = "https://api.courier.com/auth/issue-token";
                  
                  HttpHeaders headers = new HttpHeaders();
                  headers.set("Authorization", "Bearer " + courierApiKey);
                  headers.setContentType(MediaType.APPLICATION_JSON);
                  
                  Map<String, String> payload = new HashMap<>();
                  payload.put("scope", String.format("user_id:%s inbox:read:messages inbox:write:events read:preferences write:preferences read:brands", userId));
                  payload.put("expires_in", "1d");
                  
                  HttpEntity<Map<String, String>> entity = new HttpEntity<>(payload, headers);
                  ResponseEntity<Map> response = restTemplate.postForEntity(courierUrl, entity, Map.class);
                  
                  if (!response.getStatusCode().is2xxSuccessful()) {
                      return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
                          .body(Map.of("error", "Failed to generate JWT"));
                  }
                  
                  Map<String, Object> responseBody = response.getBody();
                  Map<String, Object> result = new HashMap<>();
                  result.put("token", responseBody.get("token"));
                  result.put("expiresIn", "1d");
                  
                  return ResponseEntity.ok(result);
                  
              } catch (Exception e) {
                  return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
                      .body(Map.of("error", "Failed to generate JWT", "message", e.getMessage()));
              }
          }
      }
      ```

      ```php Laravel theme={null}
      <?php

      namespace App\Http\Controllers;

      use Illuminate\Http\Request;
      use Illuminate\Support\Facades\Http;
      use Illuminate\Support\Facades\Log;

      class JwtController extends Controller
      {
          public function generateCourierJwt(Request $request)
          {
              try {
                  $userId = $request->input('userId');
                  
                  // Call Courier API to generate JWT
                  $courierApiKey = env('COURIER_API_KEY');
                  $courierUrl = 'https://api.courier.com/auth/issue-token';
                  
                  $response = Http::withHeaders([
                      'Authorization' => 'Bearer ' . $courierApiKey,
                      'Content-Type' => 'application/json',
                  ])->post($courierUrl, [
                      'scope' => "user_id:{$userId} inbox:read:messages inbox:write:events read:preferences write:preferences read:brands",
                      'expires_in' => '1d'
                  ]);
                  
                  if (!$response->successful()) {
                      return response()->json([
                          'error' => 'Failed to generate JWT',
                          'message' => 'Courier API error: ' . $response->status()
                      ], 500);
                  }
                  
                  $data = $response->json();
                  
                  return response()->json([
                      'token' => $data['token'],
                      'expiresIn' => '1d'
                  ]);
                  
              } catch (\Exception $e) {
                  Log::error('Error generating JWT: ' . $e->getMessage());
                  return response()->json([
                      'error' => 'Failed to generate JWT',
                      'message' => $e->getMessage()
                  ], 500);
              }
          }
      }
      ```

      ```ruby Ruby on Rails theme={null}
      class Api::JwtController < ApplicationController
        def generate_courier_jwt
          begin
            user_id = params[:userId]
            
            # Call Courier API to generate JWT
            courier_api_key = ENV['COURIER_API_KEY']
            courier_url = 'https://api.courier.com/auth/issue-token'
            
            response = HTTParty.post(
              courier_url,
              headers: {
                'Authorization' => "Bearer #{courier_api_key}",
                'Content-Type' => 'application/json'
              },
              body: {
                scope: "user_id:#{user_id} inbox:read:messages inbox:write:events read:preferences write:preferences read:brands",
                expires_in: '1d'
              }.to_json
            )
            
            unless response.success?
              return render json: {
                error: 'Failed to generate JWT',
                message: "Courier API error: #{response.code}"
              }, status: :internal_server_error
            end
            
            data = JSON.parse(response.body)
            
            render json: {
              token: data['token'],
              expiresIn: '1d'
            }
            
          rescue => e
            Rails.logger.error("Error generating JWT: #{e.message}")
            render json: {
              error: 'Failed to generate JWT',
              message: e.message
            }, status: :internal_server_error
          end
        end
      end
      ```
    </CodeGroup>

    <Info>
      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'`).
    </Info>
  </Step>

  <Step title="Add user validation (recommended)">
    Add validation to ensure the requesting user is authenticated and authorized.

    <CodeGroup>
      ```javascript Node.js theme={null}
      // 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' });
        }
      });
      ```

      ```python Python theme={null}
      # Example: Validate user session before generating JWT (Python/Flask)
      from flask import request, session, jsonify

      @app.route('/api/generate-courier-jwt', methods=['POST'])
      def generate_courier_jwt():
          try:
              authenticated_user_id = session.get('user_id')  # or use your auth logic
              user_id = request.json.get('userId')

              # Ensure requested userId matches authenticated user
              if not authenticated_user_id or authenticated_user_id != user_id:
                  return jsonify({'error': 'Unauthorized'}), 401

              # Continue with JWT generation...
              # (call /auth/issue-token, etc.)
          except Exception as e:
              return jsonify({'error': 'Server error'}), 500
      ```

      ```java Spring Boot theme={null}
      // Example: Validate user session before generating JWT (Java/Spring Boot)
      @PostMapping("/generate-courier-jwt")
      public ResponseEntity<?> generateCourierJwt(@RequestBody Map<String, String> request, HttpSession session) {
          try {
              // Get user ID from session/auth system
              String authenticatedUserId = (String) session.getAttribute("userId"); // or use your authentication method
              String userId = request.get("userId");
              
              // Ensure requested userId matches authenticated user
              if (authenticatedUserId == null || !authenticatedUserId.equals(userId)) {
                  return ResponseEntity.status(HttpStatus.UNAUTHORIZED)
                      .body(Map.of("error", "Unauthorized"));
              }
              
              // Continue with JWT generation...
              // (call /auth/issue-token, etc.)
          } catch (Exception e) {
              return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
                  .body(Map.of("error", "Server error"));
          }
      }
      ```

      ```php Laravel theme={null}
      // Example: Validate user session before generating JWT (PHP/Laravel)
      public function generateCourierJwt(Request $request)
      {
          try {
              // Get user ID from session/auth system
              $authenticatedUserId = $request->session()->get('user_id'); // or use your authentication method
              $userId = $request->input('userId');
              
              // Ensure requested userId matches authenticated user
              if (!$authenticatedUserId || $authenticatedUserId !== $userId) {
                  return response()->json(['error' => 'Unauthorized'], 401);
              }
              
              // Continue with JWT generation...
              // (call /auth/issue-token, etc.)
          } catch (\Exception $e) {
              return response()->json(['error' => 'Server error'], 500);
          }
      }
      ```

      ```ruby Ruby on Rails theme={null}
      # Example: Validate user session before generating JWT (Ruby on Rails)
      def generate_courier_jwt
        begin
          # Get user ID from session/auth system
          authenticated_user_id = session[:user_id] # or use your authentication method
          user_id = params[:userId]
          
          # Ensure requested userId matches authenticated user
          if authenticated_user_id.blank? || authenticated_user_id != user_id
            render json: { error: 'Unauthorized' }, status: :unauthorized and return
          end
          
          # Continue with JWT generation...
          # (call /auth/issue-token, etc.)
        rescue => e
          render json: { error: 'Server error' }, status: :internal_server_error
        end
      end
      ```
    </CodeGroup>

    <Info>
      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.
    </Info>
  </Step>

  <Step title="Start your server">
    Start your backend server and verify the endpoint is accessible.

    **Node.js:**

    ```bash theme={null}
    node server.js
    ```

    **Python:**

    ```bash theme={null}
    python app.py
    ```
  </Step>
</Steps>

<Tip>
  **See Complete SDK Implementations:**  Find ready-to-use backend JWT examples for major SDKs in [courier-samples](https://github.com/trycourier/courier-samples), including [Node.js](https://github.com/trycourier/courier-samples/tree/main/server/node/generate-jwt.js), [Python](https://github.com/trycourier/courier-samples/tree/main/server/python/generate-jwt.py), [Ruby](https://github.com/trycourier/courier-samples/tree/main/server/ruby/generate-jwt.rb), and more.
</Tip>

## 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

```javascript theme={null}
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:

```javascript theme={null}
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 />;
}
```

<Info>
  **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.
</Info>

## 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

<Warning>
  Avoid extremely long expiration times. Tokens should expire when user sessions end or shortly after.
</Warning>

### 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

<Note>
  **Learn More**: For complete API documentation, see the [Auth API Reference](/api-reference/authentication/create-a-jwt) and [Issue Token endpoint](/api-reference/authentication/create-a-jwt).
</Note>

## What's Next

<CardGroup cols={2}>
  <Card title="Implement Courier Inbox" icon="inbox" href="/tutorials/inbox/how-to-implement-inbox">
    Add an in-app notification inbox to your React application
  </Card>

  <Card title="Embed Preferences in React" icon="sliders" href="/tutorials/preferences/how-to-embed-preferences-in-react">
    Build an in-app preference center with React components
  </Card>

  <Card title="Authentication Reference" icon="lock" href="/platform/inbox/authentication">
    Deep dive into JWT scopes and token management
  </Card>

  <Card title="Auth API Reference" icon="code" href="/api-reference/authentication/create-a-jwt">
    Full API documentation for issuing tokens
  </Card>
</CardGroup>
