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

> ## Agent Instructions
> Install the Courier skill before writing code: `npx skills add trycourier/courier-skills`. It carries the verified SDK shapes and the rules you cannot get wrong.
> Authenticate every request with `Authorization: Bearer <API_KEY>`. A workspace has several environments and each has its own keys, which are plain `pk_` strings with no environment prefix. Start with Test.
> Send with `client.send.message`, the default import of the v7 Node SDK. Reference a template by its `nt_` id or its alias.
> A send accepts a bare Elemental element list, but storing content on a template requires the top-level elements wrapped in a channel element.
> Templates and journeys can be built in the Courier app or created through the API. Either way they live in the workspace and are referenced by ID when you send.
> The hosted MCP server is https://mcp.courier.com. For a briefing on what Courier is and when to use it, read https://www.courier.com/llms.txt.
> Prefer the Guides tab for how-do-I questions and the Docs tab for how-does-it-behave questions. The API reference lives under /api-reference.

# Send push with Firebase Cloud Messaging through Courier

> Connect FCM with a service account and sync device tokens with the Android, iOS, or Flutter SDK.

export const Endpoint = ({method, path, name, href, children, bare}) => {
  const verb = String(method || "").toUpperCase();
  const title = verb + " " + path;
  const label = children || name || path;
  if (bare) {
    return href ? <a href={href}><code>{title}</code></a> : <code>{title}</code>;
  }
  if (!href) {
    return <span className="cx-endpoint" data-method={verb} title={title}>
        <span className="cx-endpoint-label">{label}</span>
        <span className="cx-endpoint-method">{verb}</span>
      </span>;
  }
  return <a className="cx-endpoint" data-method={verb} href={href} title={title}>
      <span className="cx-endpoint-label">{label}</span>
      <span className="cx-endpoint-method">{verb}</span>
    </a>;
};

export const AppLink = ({href, children, name, bare}) => {
  const label = children || name || "Open in Courier";
  if (bare) {
    return <a href={href} target="_blank" rel="noreferrer">{label}</a>;
  }
  return <a className="cx-endpoint" data-kind="app" href={href} target="_blank" rel="noreferrer">
      <span className="cx-endpoint-label">{label}</span>
      <span className="cx-endpoint-method" aria-hidden="true">↗</span>
    </a>;
};

export const Guide = ({href, children, name, bare}) => {
  const label = children || name || href;
  if (bare) {
    return <a href={href}>{label}</a>;
  }
  return <a className="cx-endpoint" data-kind="guide" href={href}>
      <span className="cx-endpoint-label">{label}</span>
      <span className="cx-endpoint-method">GUIDE</span>
    </a>;
};

export const Doc = ({href, children, name, bare}) => {
  const label = children || name || href;
  if (bare) {
    return <a href={href}>{label}</a>;
  }
  return <a className="cx-endpoint" data-kind="doc" href={href}>
      <span className="cx-endpoint-label">{label}</span>
      <span className="cx-endpoint-method">DOC</span>
    </a>;
};

## Prerequisites

* [A Firebase project](https://firebase.google.com/)
* A service account private key from that project
* For iOS, an APNs key uploaded to Firebase

## Setup

### Configure FCM provider

<Steps>
  <Step title="Generate a private key">
    In your [Firebase Project](https://console.firebase.google.com/), go to "Project Settings" > "Service Accounts" and generate a new private key.
  </Step>

  <Step title="Add the key to Courier">
    Copy the contents of the downloaded private key JSON file and paste it into **Service Account JSON** in the <AppLink href="https://app.courier.com/integrations/catalog/firebase-fcm">Courier FCM provider configuration</AppLink>.
  </Step>

  <Step title="Review the remaining settings">
    * **Apply Recommended Courier Mobile SDK Formatting**, on by default. It ships the message as a `data` payload rather than `notification`, so Android wakes in the background and your own notification style applies. That background wake is also what reports the delivery back to Courier. It attaches an APNs override for Courier's <Doc href="/docs/sdk-libraries/ios#notification-service-extension">iOS Notification Service Extension</Doc> too, so an iOS device receiving through FCM still tracks delivery. See <Guide href="/docs/guides/set-up-mobile-push#delivery-and-click-tracking">Delivery and click tracking</Guide>.

    <Frame caption="The Courier FCM provider configuration with the Apply Recommended Courier Mobile SDK Formatting toggle enabled.">
      <img src="https://mintcdn.com/courier-4f1f25dc/9rcgucLA9fBnJt_U/assets/fcm-auto-override.webp?fit=max&auto=format&n=9rcgucLA9fBnJt_U&q=85&s=bb8ddd0ff4b4835b9e11ab6331075a23" alt="FCM provider configuration showing the Apply Recommended Courier Mobile SDK Formatting toggle" width="1716" height="422" data-path="assets/fcm-auto-override.webp" />
    </Frame>

    * **Bundle ID** and **Filter By Bundle ID**, off by default. Together they restrict a send to tokens whose `device.app_id` matches, so a user with two app installs only gets it on the right one.
  </Step>

  <Step title="Install the provider">
    Click "Install Provider" or "Save" to complete the configuration.
  </Step>
</Steps>

### Enable iOS support (if required)

<Steps>
  <Step title="Integrate Firebase into your iOS project">
    Integrate Firebase into your [iOS project](https://firebase.google.com/docs/ios/setup).
  </Step>

  <Step title="Select your iOS project in Cloud Messaging">
    In your Firebase project settings, go to "Cloud Messaging" and select your iOS project under "Apple app configuration".
  </Step>

  <Step title="Create an APNs key">
    Create a new key in your [Apple Developer Account](https://developer.apple.com/account) with "Apple Push Notifications Service (APNs)" enabled.
  </Step>

  <Step title="Upload the .p8 file to Firebase">
    Download the generated `.p8` file and upload it to your Firebase project settings under "Apple apps" > your app > "APNs Authentication Key".
  </Step>
</Steps>

## Profile requirements

Device tokens live on the user profile rather than on the send, so one profile can hold tokens for several devices and every push provider reads them from the same place. <Doc href="/docs/send/push/overview">Send push</Doc> covers the model.

FCM addresses the device by registration token, so the profile you send to needs a `firebaseToken`. Store the token once with <Endpoint method="POST" path="/profiles/{user_id}" name="Create a Profile" href="/docs/api-reference/user-profiles/create-a-profile" />, which merges into the profile and creates it if it does not exist:

<Tabs>
  <Tab title="One device">
    A single registration token, as a string.

    <CodeGroup>
      ```javascript Node.js highlight={3} theme={null}
      const profile = await client.profiles.create('user_123', {
        profile: {
          firebaseToken: 'YOUR_FCM_TOKEN',
        },
      });
      ```

      ```python Python highlight={4} theme={null}
      profile = client.profiles.create(
          user_id="user_123",
          profile={
              "firebaseToken": "YOUR_FCM_TOKEN",
          },
      )
      ```

      ```bash cURL highlight={7} theme={null}
      curl --request POST \
        --url https://api.courier.com/profiles/user_123 \
        --header "Authorization: Bearer $COURIER_API_KEY" \
        --header 'Content-Type: application/json' \
        --data '{
          "profile": {
            "firebaseToken": "YOUR_FCM_TOKEN"
          }
        }'
      ```

      ```ruby Ruby highlight={4} theme={null}
      profile = courier.profiles.create(
        "user_123",
        profile: {
          firebaseToken: "YOUR_FCM_TOKEN"
        }
      )
      ```

      ```go Go highlight={6} theme={null}
      profile, err := client.Profiles.New(
      	context.TODO(),
      	"user_123",
      	courier.ProfileNewParams{
      		Profile: map[string]any{
      			"firebaseToken": "YOUR_FCM_TOKEN",
      		},
      	},
      )
      ```

      ```java Java highlight={4} theme={null}
      ProfileCreateParams params = ProfileCreateParams.builder()
          .userId("user_123")
          .profile(ProfileCreateParams.Profile.builder()
              .putAdditionalProperty("firebaseToken", JsonValue.from("YOUR_FCM_TOKEN"))
              .build())
          .build();
      ProfileCreateResponse profile = client.profiles().create(params);
      ```

      ```php PHP highlight={2} theme={null}
      $profile = $client->profiles->create('user_123', profile: [
        'firebaseToken' => 'YOUR_FCM_TOKEN',
      ]);
      ```

      ```csharp C# highlight={6} theme={null}
      ProfileCreateParams parameters = new()
      {
          UserID = "user_123",
          Profile = new Dictionary<string, JsonElement>()
          {
              { "firebaseToken", JsonSerializer.SerializeToElement("YOUR_FCM_TOKEN") },
          },
      };
      var profile = await client.Profiles.Create(parameters);
      ```

      ```bash CLI highlight={4} wrap theme={null}
      courier profiles create \
        --api-key "$COURIER_API_KEY" \
        --user-id user_123 \
        --profile '{"firebaseToken":"YOUR_FCM_TOKEN"}'
      ```

      ```text MCP theme={null}
      With Courier MCP, save the FCM token YOUR_FCM_TOKEN on user_123.
      ```
    </CodeGroup>
  </Tab>

  <Tab title="Several devices">
    An array reaches every device that user signs in on.

    <CodeGroup>
      ```javascript Node.js highlight={3-6} theme={null}
      const profile = await client.profiles.create('user_123', {
        profile: {
          firebaseToken: [
            'FCM_TOKEN_ONE',
            'FCM_TOKEN_TWO',
          ],
        },
      });
      ```

      ```python Python highlight={4-7} theme={null}
      profile = client.profiles.create(
          user_id="user_123",
          profile={
              "firebaseToken": [
                  "FCM_TOKEN_ONE",
                  "FCM_TOKEN_TWO",
              ],
          },
      )
      ```

      ```bash cURL highlight={7-10} theme={null}
      curl --request POST \
        --url https://api.courier.com/profiles/user_123 \
        --header "Authorization: Bearer $COURIER_API_KEY" \
        --header 'Content-Type: application/json' \
        --data '{
          "profile": {
            "firebaseToken": [
              "FCM_TOKEN_ONE",
              "FCM_TOKEN_TWO"
            ]
          }
        }'
      ```

      ```ruby Ruby highlight={3-5} theme={null}
      profile = courier.profiles.create(
        "user_123",
        profile: {
          firebaseToken: ["FCM_TOKEN_ONE", "FCM_TOKEN_TWO"]
        }
      )
      ```

      ```go Go highlight={5-7} theme={null}
      profile, err := client.Profiles.New(
      	context.TODO(),
      	"user_123",
      	courier.ProfileNewParams{
      		Profile: map[string]any{
      			"firebaseToken": []any{"FCM_TOKEN_ONE", "FCM_TOKEN_TWO"},
      		},
      	},
      )
      ```

      ```java Java highlight={3-5} theme={null}
      ProfileCreateParams params = ProfileCreateParams.builder()
          .userId("user_123")
          .profile(ProfileCreateParams.Profile.builder()
              .putAdditionalProperty("firebaseToken", JsonValue.from(java.util.List.of("FCM_TOKEN_ONE", "FCM_TOKEN_TWO")))
              .build())
          .build();
      ProfileCreateResponse profile = client.profiles().create(params);
      ```

      ```php PHP theme={null}
      $profile = $client->profiles->create('user_123', profile: [
        'firebaseToken' => ['FCM_TOKEN_ONE', 'FCM_TOKEN_TWO'],
      ]);
      ```

      ```csharp C# highlight={7-10} theme={null}
      ProfileCreateParams parameters = new()
      {
          UserID = "user_123",
          Profile = JsonSerializer.Deserialize<Dictionary<string, JsonElement>>(
              """
              {
                "firebaseToken": [
                  "FCM_TOKEN_ONE",
                  "FCM_TOKEN_TWO"
                ]
              }
              """
          ),
      };
      var profile = await client.Profiles.Create(parameters);
      ```

      ```bash CLI highlight={4} wrap theme={null}
      courier profiles create \
        --api-key "$COURIER_API_KEY" \
        --user-id user_123 \
        --profile '{"firebaseToken":["FCM_TOKEN_ONE","FCM_TOKEN_TWO"]}'
      ```

      ```text MCP theme={null}
      With Courier MCP, save the FCM tokens FCM_TOKEN_ONE and FCM_TOKEN_TWO on user_123.
      ```
    </CodeGroup>
  </Tab>
</Tabs>

On Android the Courier SDK keeps that token current once you forward `onNewToken` to it. You send to a `user_id` and never put a token on a profile yourself.

On iOS, FCM is a token you register and refresh, because the SDK's own hook there is APNs.

For a one-off with no stored profile, pass it inline instead: `"to": { "firebaseToken": "YOUR_FCM_TOKEN" }`.

<CardGroup cols={2}>
  <Card title="Set up push notifications" icon="mobile" href="/docs/guides/set-up-mobile-push">
    Wire up the SDK, sync tokens, and send your first push.
  </Card>

  <Card title="Send by user id" icon="user" href="/docs/recipients/overview#send-by-user-id">
    The call in every language, and the rest of the profile object.
  </Card>
</CardGroup>

## Send to a recipient

<Tabs>
  <Tab title="Send to user id">
    Courier reads `firebaseToken` off the saved profile, so preferences apply and the value can change without touching this code.

    <CodeGroup>
      ```javascript Node.js highlight={4} theme={null}
      const { requestId } = await courier.send.message({
        message: {
          to: {
            user_id: "user_123",
          },
          template: "nt_01kx4h2jdafq8bk9aftxak4b40",
        },
      });
      ```

      ```python Python highlight={4} theme={null}
      response = client.send.message(
          message={
              "to": {
                  "user_id": "user_123",
              },
              "template": "nt_01kx4h2jdafq8bk9aftxak4b40",
          },
      )
      ```

      ```bash cURL highlight={7} wrap theme={null}
      curl -X POST https://api.courier.com/send \
        -H "Authorization: Bearer $COURIER_API_KEY" \
        -H "Content-Type: application/json" \
        -d '{
          "message": {
            "to": {
              "user_id": "user_123"
            },
            "template": "nt_01kx4h2jdafq8bk9aftxak4b40"
          }
        }'
      ```

      ```ruby Ruby highlight={4} theme={null}
      response = courier.send_.message(
        message: {
          to: {
            user_id: "user_123"
          },
          template: "nt_01kx4h2jdafq8bk9aftxak4b40"
        }
      )
      ```

      ```go Go highlight={5} theme={null}
      response, err := client.Send.Message(context.TODO(), courier.SendMessageParams{
      	Message: courier.SendMessageParamsMessage{
      		To: courier.SendMessageParamsMessageToUnion{
      			OfUserRecipient: &shared.UserRecipientParam{
      				UserID: courier.String("user_123"),
      			},
      		},
      		Template: courier.String("nt_01kx4h2jdafq8bk9aftxak4b40"),
      	},
      })
      ```

      ```java Java highlight={3} theme={null}
      SendMessageParams params = SendMessageParams.builder()
          .message(SendMessageParams.Message.builder()
              .to(UserRecipient.builder().userId("user_123").build())
              .template("nt_01kx4h2jdafq8bk9aftxak4b40")
              .build())
          .build();
      SendMessageResponse response = client.send().message(params);
      ```

      ```php PHP highlight={4} theme={null}
      $response = $client->send->message(
        message: [
          'to' => [
            'user_id' => 'user_123',
          ],
          'template' => 'nt_01kx4h2jdafq8bk9aftxak4b40',
        ],
      );
      ```

      ```csharp C# highlight={5} theme={null}
      SendMessageParams parameters = new()
      {
          Message = new()
          {
              To = new UserRecipient { UserID = "user_123" },
              Template = "nt_01kx4h2jdafq8bk9aftxak4b40",
          },
      };

      var response = await client.Send.Message(parameters);
      ```

      ```bash CLI highlight={3} wrap theme={null}
      courier send message \
        --api-key "$COURIER_API_KEY" \
        --message.to '{"user_id": "user_123"}' \
        --message.template nt_01kx4h2jdafq8bk9aftxak4b40
      ```

      ```text MCP theme={null}
      With Courier MCP, send my template to user_123 by email.
      ```
    </CodeGroup>
  </Tab>

  <Tab title="Send to a token">
    Pass it inline instead and nothing is stored. Swap this `to` object into the call on the other tab.

    | To address                    | Use                                                                  |
    | ----------------------------- | -------------------------------------------------------------------- |
    | One device                    | `"to": { "firebaseToken": "YOUR_FCM_TOKEN" }`                        |
    | Several devices               | `"to": { "firebaseToken": ["FCM_TOKEN_ONE", "FCM_TOKEN_TWO"] }`      |
    | A user, and a specific device | `"to": { "user_id": "user_123", "firebaseToken": "YOUR_FCM_TOKEN" }` |

    <Warning>
      Without a `user_id`, Courier logs the message under a synthetic `anon_<hash>` recipient and you lose:

      * Stored preferences and profile.
      * Managed token lookup, expiry, sign-out cleanup, and `bundleId` filtering.
      * Delivery and click events on that person's history.

      Put both a `user_id` and a token in `to` to supply your own and keep the user.
    </Warning>

    To pick among several tokens on one user, see <Doc href="/docs/integrations/push/overview#targeting-specific-devices">Targeting specific devices</Doc>.

    <CodeGroup>
      ```javascript Node.js highlight={5} theme={null}
      const { requestId } = await courier.send.message({
        message: {
          // A provider recipient is a profile field, so it sits outside the typed union.
          to: {
            firebaseToken: "YOUR_FCM_TOKEN",
          } as any,
          template: "nt_01kx4h2jdafq8bk9aftxak4b40",
        },
      });
      ```

      ```python Python highlight={4} theme={null}
      response = client.send.message(
          message={
              "to": {
                  "firebaseToken": "YOUR_FCM_TOKEN",
              },
              "template": "nt_01kx4h2jdafq8bk9aftxak4b40",
          },
      )
      ```

      ```bash cURL highlight={7} wrap theme={null}
      curl -X POST https://api.courier.com/send \
        -H "Authorization: Bearer $COURIER_API_KEY" \
        -H "Content-Type: application/json" \
        -d '{
          "message": {
            "to": {
              "firebaseToken": "YOUR_FCM_TOKEN"
            },
            "template": "nt_01kx4h2jdafq8bk9aftxak4b40"
          }
        }'
      ```

      ```ruby Ruby highlight={4} theme={null}
      response = courier.send_.message(
        message: {
          to: {
            firebaseToken: "YOUR_FCM_TOKEN"
          },
          template: "nt_01kx4h2jdafq8bk9aftxak4b40"
        }
      )
      ```

      ```go Go highlight={3} theme={null}
      // A provider recipient is a profile field, so it sits outside the typed union.
      to := param.Override[shared.UserRecipientParam](
      	json.RawMessage(`{"firebaseToken": "YOUR_FCM_TOKEN"}`),
      )

      response, err := client.Send.Message(context.TODO(), courier.SendMessageParams{
      	Message: courier.SendMessageParamsMessage{
      		To: courier.SendMessageParamsMessageToUnion{OfUserRecipient: &to},
      		Template: courier.String("nt_01kx4h2jdafq8bk9aftxak4b40"),
      	},
      })
      ```

      ```java Java highlight={3} theme={null}
      // A provider recipient is a profile field, so it sits outside the typed union.
      UserRecipient to = UserRecipient.builder()
          .putAdditionalProperty("firebaseToken", JsonValue.from("YOUR_FCM_TOKEN"))
          .build();

      client.send().message(SendMessageParams.builder()
          .message(SendMessageParams.Message.builder()
              .to(to)
              .template("nt_01kx4h2jdafq8bk9aftxak4b40")
              .build())
          .build());
      ```

      ```php PHP highlight={4} theme={null}
      $response = $client->send->message(
        message: [
          'to' => [
            'firebaseToken' => 'YOUR_FCM_TOKEN',
          ],
          'template' => 'nt_01kx4h2jdafq8bk9aftxak4b40',
        ],
      );
      ```

      ```csharp C# highlight={4} theme={null}
      // A provider recipient is a profile field, so it sits outside the typed union.
      UserRecipient to = UserRecipient.FromRawUnchecked(
          JsonSerializer.Deserialize<Dictionary<string, JsonElement>>(
              """{"firebaseToken": "YOUR_FCM_TOKEN"}"""
          )
      );

      var response = await client.Send.Message(new() { Message = new() { To = to, Template = "nt_01kx4h2jdafq8bk9aftxak4b40" } });
      ```

      ```bash CLI highlight={3} wrap theme={null}
      courier send message \
        --api-key "$COURIER_API_KEY" \
        --message.to '{"firebaseToken": "YOUR_FCM_TOKEN"}' \
        --message.template nt_01kx4h2jdafq8bk9aftxak4b40
      ```

      ```text MCP theme={null}
      With Courier MCP, send my template to the FCM token YOUR_FCM_TOKEN.
      ```
    </CodeGroup>
  </Tab>
</Tabs>

## Overrides

<Doc href="/docs/send/overrides#how-overrides-work">How overrides work</Doc> covers the two levels and which one wins. <Doc href="/docs/integrations/push/overview#channel-overrides">Push channel overrides</Doc> lists the fields every push provider takes.

An override changes the request Courier sends to FCM. Two paths cover most cases:

* `providers.firebase-fcm.override.body.data.YOUR_CUSTOM_KEY` adds custom data, usually to open a screen on tap. Firebase requires `data` to be flat, and override values merge in raw, so every value must already be a string. See [Firebase's data field reference](https://firebase.google.com/docs/reference/fcm/rest/v1/projects.messages#Message.FIELDS.data).
* `providers.firebase-fcm.override.body.apns` applies iOS-specific values. See [Apple's remote notification guide](https://developer.apple.com/documentation/usernotifications/setting_up_a_remote_notification_server/generating_a_remote_notification).

`providers.firebase-fcm.override.config` replaces the integration's own settings for a single send:

| Key                  | What it replaces                                |
| -------------------- | ----------------------------------------------- |
| `serviceAccountJSON` | The service account this send authenticates as. |
| `remapData`          | Whether Courier remaps `data` for this send.    |

<Warning>
  Put personalization variables in `message.data`, not in `to`. Courier reads anything nested in `to` as profile data, so it fills no template variables and triggers no data mapping.
</Warning>

Custom data not reaching the device? <Doc href="/docs/design/templates/data-mapping">Data mapping</Doc>
covers the one template setting that stops it.

### Override the payload

<CodeGroup>
  ```javascript Node.js highlight={15-29} theme={null}
  const { requestId } = await courier.send.message({
    message: {
      to: {
        user_id: "user_123",
      },
      template: "nt_01kx4h2jdafq8bk9aftxak4b40",
      routing: {
        method: "single",
        channels: [
          "push",
        ],
      },
      providers: {
        "firebase-fcm": {
          override: {
            body: {
              data: {
                YOUR_CUSTOM_KEY: "YOUR_CUSTOM_VALUE",
              },
              apns: {
                payload: {
                  aps: {
                    sound: "ping.aiff",
                    badge: 99,
                  },
                },
              },
            },
          },
        },
      },
    },
  });
  ```

  ```python Python highlight={15-29} theme={null}
  response = client.send.message(
      message={
          "to": {
              "user_id": "user_123",
          },
          "template": "nt_01kx4h2jdafq8bk9aftxak4b40",
          "routing": {
              "method": "single",
              "channels": [
                  "push",
              ],
          },
          "providers": {
              "firebase-fcm": {
                  "override": {
                      "body": {
                          "data": {
                              "YOUR_CUSTOM_KEY": "YOUR_CUSTOM_VALUE",
                          },
                          "apns": {
                              "payload": {
                                  "aps": {
                                      "sound": "ping.aiff",
                                      "badge": 99,
                                  },
                              },
                          },
                      },
                  },
              },
          },
      },
  )
  ```

  ```bash cURL highlight={18-32} wrap theme={null}
  curl -X POST https://api.courier.com/send \
    -H "Authorization: Bearer $COURIER_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{
      "message": {
        "to": {
          "user_id": "user_123"
        },
        "template": "nt_01kx4h2jdafq8bk9aftxak4b40",
        "routing": {
          "method": "single",
          "channels": [
            "push"
          ]
        },
        "providers": {
          "firebase-fcm": {
            "override": {
              "body": {
                "data": {
                  "YOUR_CUSTOM_KEY": "YOUR_CUSTOM_VALUE"
                },
                "apns": {
                  "payload": {
                    "aps": {
                      "sound": "ping.aiff",
                      "badge": 99
                    }
                  }
                }
              }
            }
          }
        }
      }
    }'
  ```

  ```ruby Ruby highlight={15-29} theme={null}
  response = courier.send_.message(
    message: {
      to: {
        user_id: "user_123"
      },
      template: "nt_01kx4h2jdafq8bk9aftxak4b40",
      routing: {
        method: "single",
        channels: [
          "push"
        ]
      },
      providers: {
        "firebase-fcm": {
          override: {
            body: {
              data: {
                YOUR_CUSTOM_KEY: "YOUR_CUSTOM_VALUE"
              },
              apns: {
                payload: {
                  aps: {
                    sound: "ping.aiff",
                    badge: 99
                  }
                }
              }
            }
          }
        }
      }
    }
  )
  ```

  ```go Go highlight={17-31} theme={null}
  response, err := client.Send.Message(context.TODO(), courier.SendMessageParams{
  	Message: courier.SendMessageParamsMessage{
  		To: courier.SendMessageParamsMessageToUnion{
  			OfUserRecipient: &shared.UserRecipientParam{
  				UserID: courier.String("user_123"),
  			},
  		},
  		Template: courier.String("nt_01kx4h2jdafq8bk9aftxak4b40"),
  		Routing: courier.SendMessageParamsMessageRouting{
  			Channels: []shared.MessageRoutingChannelUnionParam{
  				{OfString: courier.String("push")},
  			},
  			Method: "single",
  		},
  		Providers: shared.MessageProvidersParam{
  			"firebase-fcm": shared.MessageProvidersTypeParam{
  				Override: map[string]any{
  					"body": map[string]any{
  						"data": map[string]any{
  							"YOUR_CUSTOM_KEY": "YOUR_CUSTOM_VALUE",
  						},
  						"apns": map[string]any{
  							"payload": map[string]any{
  								"aps": map[string]any{
  									"sound": "ping.aiff",
  									"badge": 99,
  								},
  							},
  						},
  					},
  				},
  			},
  		},
  	},
  })
  ```

  ```java Java highlight={10-24} theme={null}
  SendMessageParams params = SendMessageParams.builder()
      .message(SendMessageParams.Message.builder()
          .to(UserRecipient.builder().userId("user_123").build())
          .template("nt_01kx4h2jdafq8bk9aftxak4b40")
          .routing(SendMessageParams.Message.Routing.builder()
              .addChannel("push")
              .method(SendMessageParams.Message.Routing.Method.SINGLE)
              .build())
          .providers(MessageProviders.builder()
              .putAdditionalProperty("firebase-fcm", JsonValue.from(java.util.Map.of("override", java.util.Map.of(
                      "body", java.util.Map.of(
                          "data", java.util.Map.of(
                              "YOUR_CUSTOM_KEY", "YOUR_CUSTOM_VALUE"
                          ),
                          "apns", java.util.Map.of(
                              "payload", java.util.Map.of(
                                  "aps", java.util.Map.of(
                                      "sound", "ping.aiff",
                                      "badge", 99
                                  )
                              )
                          )
                      )
                  ))))
              .build())
          .build())
      .build();
  SendMessageResponse response = client.send().message(params);
  ```

  ```php PHP highlight={15-29} theme={null}
  $response = $client->send->message(
    message: [
      'to' => [
        'user_id' => 'user_123',
      ],
      'template' => 'nt_01kx4h2jdafq8bk9aftxak4b40',
      'routing' => [
        'method' => 'single',
        'channels' => [
          'push',
        ],
      ],
      'providers' => [
        'firebase-fcm' => [
          'override' => [
            'body' => [
              'data' => [
                'YOUR_CUSTOM_KEY' => 'YOUR_CUSTOM_VALUE',
              ],
              'apns' => [
                'payload' => [
                  'aps' => [
                    'sound' => 'ping.aiff',
                    'badge' => 99,
                  ],
                ],
              ],
            ],
          ],
        ],
      ],
    ],
  );
  ```

  ```csharp C# highlight={14-32} theme={null}
  SendMessageParams parameters = new()
  {
      Message = new()
      {
          To = new UserRecipient { UserID = "user_123" },
          Template = "nt_01kx4h2jdafq8bk9aftxak4b40",
          Routing = new() { Channels = ["push"], Method = Send::Method.Single },
          Providers = new Dictionary<string, MessageProvidersType>()
          {
              {
                  "firebase-fcm",
                  new()
                  {
                      Override = JsonSerializer.Deserialize<Dictionary<string, JsonElement>>(
                          """
                          {
                            "body": {
                              "data": {
                                "YOUR_CUSTOM_KEY": "YOUR_CUSTOM_VALUE"
                              },
                              "apns": {
                                "payload": {
                                  "aps": {
                                    "sound": "ping.aiff",
                                    "badge": 99
                                  }
                                }
                              }
                            }
                          }
                          """
                      ),
                  }
              },
          },
      },
  };

  var response = await client.Send.Message(parameters);
  ```

  ```bash CLI highlight={6} wrap theme={null}
  courier send message \
    --api-key "$COURIER_API_KEY" \
    --message.to '{"user_id": "user_123"}' \
    --message.template nt_01kx4h2jdafq8bk9aftxak4b40 \
    --message.routing '{"method": "single", "channels": ["push"]}' \
    --message.providers '{"firebase-fcm": {"override": {"body": {"data": {"YOUR_CUSTOM_KEY": "YOUR_CUSTOM_VALUE"}, "apns": {"payload": {"aps": {"sound": "ping.aiff", "badge": 99}}}}}}}'
  ```

  ```text MCP theme={null}
  With Courier MCP, send my template to user_123 on push, with an FCM sound and badge.
  ```
</CodeGroup>

## Provider details

```text theme={null}
firebase-fcm
```

Courier recommends routing to the channel. Naming this key in `routing.channels` instead is supported, and sends through just this provider.

<Card title="Send to a specific provider" icon="bullseye-arrow" href="/docs/send/send-to-a-provider" horizontal arrow="true">
  When that is worth doing, and what you give up: failover, channel priority, and providers you add later.
</Card>
