Sign in Create account
Integrations

Firebase Auth

Two Cloud Functions tell Postal about every user created or deleted in Firebase Authentication.

Firebase Authentication has no webhooks. The integration uses two Cloud Functions (triggers for user created and user deleted) that call the connection URL with its secret.

#Create the connection

In the dashboard, open Integrations › Your app and click Connect on the Firebase Auth card. The connection has its own URL, which you paste into the provider, and it shows the step-by-step instructions on the same screen.

Connection URL (example)
https://api.postal.so/integrations/crm/01a1c2d3-e4f5-7a6b-8c9d-0e1f2a3b4c5d/webhook

The connection shows the secret and the code, already filled in with your URL. Store the secret in Firebase:

Terminal
firebase functions:secrets:set POSTAL_SECRET

App integrations count toward your plan limit (app_integrations). If they are not in your plan, the cards appear locked with an Upgrade button.

#The Cloud Functions

functions/index.js
const functions = require('firebase-functions/v1')

const POSTAL_URL = 'https://api.postal.so/integrations/crm/<connection id>/webhook'

async function sendToPostal(event, user) {
  const response = await fetch(POSTAL_URL, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json', 'X-Postal-Secret': process.env.POSTAL_SECRET },
    body: JSON.stringify({
      event,
      user: { uid: user.uid, email: user.email, displayName: user.displayName, emailVerified: user.emailVerified },
    }),
  })

  if (!response.ok) {
    throw new Error(`Postal responded with ${response.status}`)
  }
}

const withSecret = functions.runWith({ secrets: ['POSTAL_SECRET'] })

exports.postalUserCreated = withSecret.auth.user().onCreate((user) => sendToPostal('user.created', user))
exports.postalUserDeleted = withSecret.auth.user().onDelete((user) => sendToPostal('user.deleted', user))
Terminal
firebase deploy --only functions

The functions use the fetch of Node.js 18 or newer. This is the accepted format, so you can also call the URL from elsewhere in your backend:

Body
{"event": "user.created", "user": {"uid": "abc123", "email": "[email protected]", "displayName": "Jane Smith", "emailVerified": false}}

#Events

EventPostal eventContact
user.createdfirebase.user.createdCreated with the email and the name (displayName); the uid becomes external_id.
user.deletedfirebase.user.deleted (a deletion)By the uid; never creates a contact.

For deletions, the connection says what to do with the contact: only record the event (the default), unsubscribe the contact (it stays in the audience, but receives no marketing) or delete the contact (no event, since the contact is gone). A deletion never creates a contact that Postal did not have.

#Connection settings

  • Events: only the checked ones become a contact and an event; the others are ignored (the response is still 202).
  • Sync the name: updates the contact's first and last name with what the provider sends.
  • Add to segment and Subscribe to topic: a static segment and a topic for every contact that arrives.
  • When the user is deleted in the app: only record the event, unsubscribe the contact or delete it.

Everything the connection changes on a contact carries the source crm:<connection id> (last_change_source), and each delivery appears in the connection's log, with the result for each person. A delivery that the provider repeats (same ID) is not processed twice.

#Trigger automations

Each event is stored on the contact under the name firebase.user.created, with the properties from the table above. On the connection screen, the Create automation button next to each event opens a new automation with the Event received trigger already filled in. Through the automations endpoints, the trigger is {"type": "event", "event": "firebase.user.created"}.

Example: a welcome sequence starting at sign-up. In the emails, the event properties are available as {{event.amount}} in the steps or as template variables ({{{amount}}}).