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

# Expo Push Push Integration with Novu

> Connect Expo Push to Novu to send push notifications to Expo-managed iOS and Android apps through workflows using Expo push tokens and access credentials.

This guide explains the process of configuring and using Expo Push with Novu, from getting your credentials to sending your first notification.

## How to configure Expo with Novu

Before you can send notifications, you must connect your Expo project to Novu by generating an access token and adding it to your integration settings.

### Step 1: Generate your access token from Expo Push

Generate an access token from your dashboard. This token authorizes Novu to send notifications on behalf of your project.

<Steps>
  <Step title="Log in to Expo">
    Sign in at the [Expo console](https://expo.dev/).
  </Step>

  <Step title="Open Access Token">
    Navigate to the **Credentials** section in the project settings sidebar.
    Click [**Access Token**](https://expo.dev/settings/access-tokens).

    <img src="https://mintcdn.com/novu-c5de82d9/J466heAwyEbvOhEB/images/channels-and-providers/push/expo-push/create-token.png?fit=max&auto=format&n=J466heAwyEbvOhEB&q=85&s=15481110d34b91ea7d851f6211089bd1" alt="Create Expo token" width="2880" height="1622" data-path="images/channels-and-providers/push/expo-push/create-token.png" />
  </Step>

  <Step title="Create a token">
    A menu appears.
  </Step>

  <Step title="Generate the token">
    Give it a descriptive name, and then click **Generate New Token**.

    <img src="https://mintcdn.com/novu-c5de82d9/J466heAwyEbvOhEB/images/channels-and-providers/push/expo-push/generate-token.png?fit=max&auto=format&n=J466heAwyEbvOhEB&q=85&s=5ec86d3cc224c57a1e69565860a9f664" alt="Generate Expo token" width="2880" height="1624" data-path="images/channels-and-providers/push/expo-push/generate-token.png" />
  </Step>

  <Step title="Copy the token">
    Copy and save the generated access token. You need it in the next step.

    <img src="https://mintcdn.com/novu-c5de82d9/J466heAwyEbvOhEB/images/channels-and-providers/push/expo-push/copy-token.png?fit=max&auto=format&n=J466heAwyEbvOhEB&q=85&s=c68ec16cf273d5665afff9db0c8736c7" alt="Copy Expo token" width="2880" height="1624" data-path="images/channels-and-providers/push/expo-push/copy-token.png" />
  </Step>
</Steps>

### Step 2: Connect Expo Push to Novu

Next, add the access token to your Expo integration in the Novu dashboard.

<Steps>
  <Step title="Log in to the Novu dashboard">
    Open the [Novu Dashboard](https://dashboard.novu.co).
  </Step>

  <Step title="Open Integration Store">
    On the Novu dashboard, navigate to the **Integration Store**.
  </Step>

  <Step title="Connect a provider">
    In the **Integration Store**, click **Connect provider** to begin setup.
  </Step>

  <Step title="Select Expo Push">
    In the **Push** tab, choose **Expo Push** from the provider list.
  </Step>

  <Step title="Paste the access token">
    In the Expo integration form, paste the access token that you copied from Expo into the **Access Token** field.

    <img src="https://mintcdn.com/novu-c5de82d9/J466heAwyEbvOhEB/images/channels-and-providers/push/expo-push/expo-integration.png?fit=max&auto=format&n=J466heAwyEbvOhEB&q=85&s=43f69158f18a3d24f1e2c91cccf60859" alt="Expo Integration in Novu" width="2880" height="1624" data-path="images/channels-and-providers/push/expo-push/expo-integration.png" />
  </Step>

  <Step title="Create the integration">
    Review your credentials, then click **Create Integration** to save.
  </Step>
</Steps>

## Using Expo Push with Novu

Once your integration is configured, you can start sending push notifications by registering your subscribers' device tokens and triggering a workflow.

### Step 1: Add subscriber device token

Before Novu can send a push notification to a subscriber (user), you must associate their device's unique push token with their Novu subscriber profile.

You can do this by making an API call to [update the subscriber's credentials](/api-reference/subscribers/update-provider-credentials).

<Tabs>
  <Tab title="Node.js">
    ```typescript theme={null}
    import { Novu } from '@novu/api';
    import { ChatOrPushProviderEnum } from "@novu/api/models/components";

    const novu = new Novu({ secretKey: "<NOVU_SECRET_KEY>" });

    await novu.subscribers.credentials.update(
      {
        providerId: ChatOrPushProviderEnum.Expo,
        integrationIdentifier: "string",
        credentials: { deviceTokens: ["token1", "token2"] },
      },
      "subscriberId"
    );
    ```
  </Tab>

  <Tab title="Python">
    ```python theme={null}
    import os
    import novu_py
    from novu_py import Novu

    with Novu(secret_key=os.getenv("NOVU_SECRET_KEY", "")) as novu:
        novu.subscribers.credentials.update(
            subscriber_id="subscriberId",
            update_subscriber_channel_request_dto={
                "provider_id": novu_py.ChatOrPushProviderEnum.EXPO,
                "credentials": {"deviceTokens": ["token1", "token2"]},
                "integration_identifier": "string",
            },
        )
    ```
  </Tab>

  <Tab title="Go">
    ```go theme={null}
    import (
        "context"
        "os"

        novugo "github.com/novuhq/novu-go"
        "github.com/novuhq/novu-go/models/components"
    )

    s := novugo.New(novugo.WithSecurity(os.Getenv("NOVU_SECRET_KEY")))

    res, err := s.Subscribers.Credentials.Update(context.Background(), "subscriberId", components.UpdateSubscriberChannelRequestDto{
        ProviderID: components.ChatOrPushProviderEnumExpo,
            IntegrationIdentifier: novugo.String("string"),
        Credentials: components.ChannelCredentials{
            DeviceTokens: []string{"token1", "token2"},
        },
    }, nil)
    ```
  </Tab>

  <Tab title="PHP">
    ```php theme={null}
    use novu;
    use novu\Models\Components;

    $sdk = novu\Novu::builder()->setSecurity('<NOVU_SECRET_KEY>')->build();

    $sdk->subscribersCredentials->update(
        subscriberId: 'subscriberId',
        updateSubscriberChannelRequestDto: new Components\UpdateSubscriberChannelRequestDto(
            providerId: Components\ChatOrPushProviderEnum::Expo,
            integrationIdentifier: 'string',
            credentials: new Components\ChannelCredentials(
                deviceTokens: ['token1', 'token2'],
            ),
        ),
    );
    ```
  </Tab>

  <Tab title=".NET">
    ```csharp theme={null}
    using Novu;
    using Novu.Models.Components;
    using System.Collections.Generic;

    var sdk = new NovuSDK(secretKey: "<NOVU_SECRET_KEY>");

    await sdk.Subscribers.Credentials.UpdateAsync(
        subscriberId: "subscriberId",
        updateSubscriberChannelRequestDto: new UpdateSubscriberChannelRequestDto() {
            ProviderId = ChatOrPushProviderEnum.Expo,
            IntegrationIdentifier = "string",
            Credentials = new ChannelCredentials() {
                DeviceTokens = new List<string> { "token1", "token2" },
            },
        });
    ```
  </Tab>

  <Tab title="Java">
    ```java theme={null}
    import co.novu.Novu;
    import co.novu.models.components.*;
    import java.util.List;

    Novu novu = Novu.builder().secretKey("<NOVU_SECRET_KEY>").build();

    novu.subscribers().credentials().update()
        .subscriberId("subscriberId")
        .body(UpdateSubscriberChannelRequestDto.builder()
            .providerId(ChatOrPushProviderEnum.EXPO)
                .integrationIdentifier("string")
            .credentials(ChannelCredentials.builder()
                .deviceTokens(List.of("token1", "token2"))
                .build())
            .build())
        .call();
    ```
  </Tab>

  <Tab title="cURL">
    ```bash theme={null}
    curl -L -X PUT 'https://api.novu.co/v1/subscribers/<SUBSCRIBER_ID>/credentials' \
    -H 'Content-Type: application/json' \
    -H 'Authorization: ApiKey <NOVU_SECRET_KEY>' \
    -d '{
      "providerId": "expo",
      "credentials": {
        "deviceTokens": [
          "token1",
          "token2"
        ]
      },
      "integrationIdentifier": "string"
    }'
    ```
  </Tab>
</Tabs>

<Note>
  Novu automatically removes invalid device tokens from a subscribers' profile and then sends the failure details to the `MESSAGE_FAILED` webhook.
</Note>

### Step 2: Send a notification

Now you're ready to send a push notification.

<Tabs>
  <Tab title="Node.js">
    ```typescript theme={null}
    import { Novu } from '@novu/api';

    const novu = new Novu({ secretKey: "<NOVU_SECRET_KEY>" });

    await novu.trigger({
      workflowId: "workflowId",
      to: { subscriberId: "SUBSCRIBER_ID", },
      payload: {},
    });
    ```
  </Tab>

  <Tab title="Python">
    ```python theme={null}
    import os
    import novu_py
    from novu_py import Novu

    with Novu(secret_key=os.getenv("NOVU_SECRET_KEY", "")) as novu:
        novu.trigger(trigger_event_request_dto=novu_py.TriggerEventRequestDto(
            workflow_id="workflowId",
            to={"subscriber_id": "SUBSCRIBER_ID"},
            payload={},
        ))
    ```
  </Tab>

  <Tab title="Go">
    ```go theme={null}
    import (
        "context"
        "os"

        novugo "github.com/novuhq/novu-go"
        "github.com/novuhq/novu-go/models/components"
    )

    s := novugo.New(novugo.WithSecurity(os.Getenv("NOVU_SECRET_KEY")))

    res, err := s.Trigger(context.Background(), components.TriggerEventRequestDto{
        WorkflowID: "workflowId",
        To: components.CreateToSubscriberPayloadDto(components.SubscriberPayloadDto{
            SubscriberID: "SUBSCRIBER_ID",
        }),
        Payload: map[string]any{},
    }, nil)
    ```
  </Tab>

  <Tab title="PHP">
    ```php theme={null}
    use novu;
    use novu\Models\Components;

    $sdk = novu\Novu::builder()->setSecurity('<NOVU_SECRET_KEY>')->build();

    $sdk->trigger(
        triggerEventRequestDto: new Components\TriggerEventRequestDto(
            workflowId: 'workflowId',
            to: new Components\SubscriberPayloadDto(subscriberId: 'SUBSCRIBER_ID'),
            payload: {},
        ),
    );
    ```
  </Tab>

  <Tab title=".NET">
    ```csharp theme={null}
    using Novu;
    using Novu.Models.Components;

    var sdk = new NovuSDK(secretKey: "<NOVU_SECRET_KEY>");

    await sdk.TriggerAsync(triggerEventRequestDto: new TriggerEventRequestDto() {
        WorkflowId = "workflowId",
        To = To.CreateSubscriberPayloadDto(new SubscriberPayloadDto() { SubscriberId = "SUBSCRIBER_ID" }),
        Payload = {},
    });
    ```
  </Tab>

  <Tab title="Java">
    ```java theme={null}
    import co.novu.Novu;
    import co.novu.models.components.*;

    Novu novu = Novu.builder().secretKey("<NOVU_SECRET_KEY>").build();

    novu.trigger()
        .body(TriggerEventRequestDto.builder()
            .workflowId("workflowId")
            .to(To2.of(SubscriberPayloadDto.builder().subscriberId("SUBSCRIBER_ID").build()))
            .payload({})
            .build())
        .call();
    ```
  </Tab>

  <Tab title="cURL">
    ```bash theme={null}
    curl --location 'https://api.novu.co/v1/events/trigger' \
    --header 'Content-Type: application/json' \
    --header 'Authorization: ApiKey <NOVU_SECRET_KEY>' \
    -d '{
        "name": "workflowId",
        "to": [
            "SUBSCRIBER_ID"
        ],
        "payload": {}
    }'
    ```
  </Tab>
</Tabs>

## Configure Expo overrides

Save an Expo override on the Push step when you need Expo Push fields beyond the step subject and body — for example `data`, `sound`, `priority`, or `ttl`. The override is stored on the step, so it is versioned and promoted with the workflow, and every trigger sends it without changing your trigger call.

<Note>
  Provider content overrides in the workflow editor are rolling out gradually and may not be available on your Push step yet. Overrides passed at trigger time work regardless. See [provider content overrides](/platform/integrations/push#provider-content-overrides).
</Note>

Expo overrides are validated against the [Expo Push message](https://docs.expo.dev/push-notifications/sending-notifications) request format. The editor autocompletes supported field names, validates the JSON, and flags unsupported fields.

* **`to` is managed by Novu.** Novu fills device tokens from the subscriber's credentials — do not set `to` in the override.
* **`body` falls back from the step.** If your override omits `body`, Novu uses the rendered Push step body.
* **`title` is not seeded.** Set `title` in the override when you want one; Novu does not copy the step subject automatically.

See the [Expo Push message format](https://docs.expo.dev/push-notifications/sending-notifications) for the full field list.

## Using overrides to customize notifications

<Tabs>
  <Tab title="Node.js">
    ```typescript theme={null}
    import { Novu } from '@novu/api';

    const novu = new Novu({ secretKey: "<NOVU_SECRET_KEY>" });

    await novu.trigger({
      workflowId: "workflowId",
      to: { subscriberId: "subscriber-id-123", },
      payload: {
        "orderId": "12345"
    },
      overrides: {
        "providers": {
            "expo": {
                "title": "Order Update",
                "body": "Your order #12345 has been shipped!",
                "data": {
                    "deepLink": "myapp://orders/12345",
                    "orderId": "12345"
                },
                "sound": "default",
                "priority": "high",
                "ttl": 3600
            }
        }
    },
    });
    ```
  </Tab>

  <Tab title="Python">
    ```python theme={null}
    import os
    import novu_py
    from novu_py import Novu

    with Novu(secret_key=os.getenv("NOVU_SECRET_KEY", "")) as novu:
        novu.trigger(trigger_event_request_dto=novu_py.TriggerEventRequestDto(
            workflow_id="workflowId",
            to={"subscriber_id": "subscriber-id-123"},
            payload={
            "orderId": "12345"
    },
            overrides={
            "providers": {
                    "expo": {
                            "title": "Order Update",
                            "body": "Your order #12345 has been shipped!",
                            "data": {
                                    "deepLink": "myapp://orders/12345",
                                    "orderId": "12345"
                            },
                            "sound": "default",
                            "priority": "high",
                            "ttl": 3600
                    }
            }
    },
        ))
    ```
  </Tab>

  <Tab title="Go">
    ```go theme={null}
    import (
        "context"
        "os"

        novugo "github.com/novuhq/novu-go"
        "github.com/novuhq/novu-go/models/components"
    )

    s := novugo.New(novugo.WithSecurity(os.Getenv("NOVU_SECRET_KEY")))

    res, err := s.Trigger(context.Background(), components.TriggerEventRequestDto{
        WorkflowID: "workflowId",
        To: components.CreateToSubscriberPayloadDto(components.SubscriberPayloadDto{
            SubscriberID: "subscriber-id-123",
        }),
        Payload: map[string]any{
        "orderId": "12345"
    },
        Overrides: map[string]any{
        "providers": {
            "expo": {
                "title": "Order Update",
                "body": "Your order #12345 has been shipped!",
                "data": {
                    "deepLink": "myapp://orders/12345",
                    "orderId": "12345"
                },
                "sound": "default",
                "priority": "high",
                "ttl": 3600
            }
        }
    },
    }, nil)
    ```
  </Tab>

  <Tab title="PHP">
    ```php theme={null}
    use novu;
    use novu\Models\Components;

    $sdk = novu\Novu::builder()->setSecurity('<NOVU_SECRET_KEY>')->build();

    $sdk->trigger(
        triggerEventRequestDto: new Components\TriggerEventRequestDto(
            workflowId: 'workflowId',
            to: new Components\SubscriberPayloadDto(subscriberId: 'subscriber-id-123'),
            payload: {
            "orderId": "12345"
    },
            overrides: {
            "providers": {
                    "expo": {
                            "title": "Order Update",
                            "body": "Your order #12345 has been shipped!",
                            "data": {
                                    "deepLink": "myapp://orders/12345",
                                    "orderId": "12345"
                            },
                            "sound": "default",
                            "priority": "high",
                            "ttl": 3600
                    }
            }
    },
        ),
    );
    ```
  </Tab>

  <Tab title=".NET">
    ```csharp theme={null}
    using Novu;
    using Novu.Models.Components;

    var sdk = new NovuSDK(secretKey: "<NOVU_SECRET_KEY>");

    await sdk.TriggerAsync(triggerEventRequestDto: new TriggerEventRequestDto() {
        WorkflowId = "workflowId",
        To = To.CreateSubscriberPayloadDto(new SubscriberPayloadDto() { SubscriberId = "subscriber-id-123" }),
        Payload = {
        "orderId": "12345"
    },
        Overrides = {
        "providers": {
            "expo": {
                "title": "Order Update",
                "body": "Your order #12345 has been shipped!",
                "data": {
                    "deepLink": "myapp://orders/12345",
                    "orderId": "12345"
                },
                "sound": "default",
                "priority": "high",
                "ttl": 3600
            }
        }
    },
    });
    ```
  </Tab>

  <Tab title="Java">
    ```java theme={null}
    import co.novu.Novu;
    import co.novu.models.components.*;

    Novu novu = Novu.builder().secretKey("<NOVU_SECRET_KEY>").build();

    novu.trigger()
        .body(TriggerEventRequestDto.builder()
            .workflowId("workflowId")
            .to(To2.of(SubscriberPayloadDto.builder().subscriberId("subscriber-id-123").build()))
            .payload({
            "orderId": "12345"
    })
            .overrides({
            "providers": {
                    "expo": {
                            "title": "Order Update",
                            "body": "Your order #12345 has been shipped!",
                            "data": {
                                    "deepLink": "myapp://orders/12345",
                                    "orderId": "12345"
                            },
                            "sound": "default",
                            "priority": "high",
                            "ttl": 3600
                    }
            }
    })
            .build())
        .call();
    ```
  </Tab>

  <Tab title="cURL">
    ```bash theme={null}
    curl --location 'https://api.novu.co/v1/events/trigger' \
    --header 'Content-Type: application/json' \
    --header 'Authorization: ApiKey <NOVU_SECRET_KEY>' \
    -d '{
        "name": "workflowId",
        "to": [
            "subscriber-id-123"
        ],
        "payload": {
            "orderId": "12345"
        },
        "overrides": {
            "providers": {
                "expo": {
                    "title": "Order Update",
                    "body": "Your order #12345 has been shipped!",
                    "data": {
                        "deepLink": "myapp://orders/12345",
                        "orderId": "12345"
                    },
                    "sound": "default",
                    "priority": "high",
                    "ttl": 3600
                }
            }
        }
    }'
    ```
  </Tab>
</Tabs>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.