Search code examples
iosnode.jstwilioserverless

Twilio - Push notifications fail in serverless production deployment


I have an Twilio programmable messaging app which is deployed in a Twilio serverless environment.

let response = new Twilio.Response();
exports.handler = async function(context, event, callback) {

    const accountSid = context.ACCOUNT_SID
    const authToken = context.AUTH_TOKEN

    const twilioClient = context.getTwilioClient();

    const toAddress = event.to;
    const fromAddress = event.from

    var text = "something something";
    
    await twilioClient.messages
    .create({ 
      body: text, 
      to: toAddress, 
      from: fromAddress
    })
    .then(...)
    .catch(...)

    response.setStatusCode(200);

    callback(null, response);
}

Production and Development have distinct sets of environment variables that point to their own respective sandbox/production push certs

ACCOUNT_SID=ACxxx
AUTH_TOKEN=xxx
API_KEY_SID=SKxxx
API_SECRET=xxx
APP_SID=APxxx
PUSH_CREDENTIAL_SID=CRxxx
CONVERSATIONS_SID=ISxxx
APN_PUSH_CREDENTIAL_SID=CRxxx

This works perfectly in development and push notifications are triggered and sent to the relevant iOS device.

Once deployed to production and used with a TestFlight build it seems like the server context isn't using the correct certificate credentials. I end up with the errors

  • 52004 - Credential SID not specified
  • 52134 - Invalid APNs device token

I know the push certificates are correct as iOS client-to-client chats work in both dev and prod and push notifications are sent and arrive as expected.

My question is, what secret sauce do I need to bind/attach the production push credential to the Twilio client when used from the serverless context.


Solution

  • The fault was due to a configuration issue in Twilio.

    1. An Account has one global Conversations module. That module has default settings for Conversations service & Messaging service
    2. Messaging configured for automatic conversation creation will always use the default Conversation service. There's no way to assign a particular Conversation service to a Messaging service.

    enter image description here

    Practically this means that there's no way to have debug and production in the same account for certain setups.

    The issue can be worked around by switching between your conversation service instances e.g:

    const twilioClient = context.getTwilioClient();
    
    const configuration_post = await twilioClient.conversations.v1
        .configuration()
        .update({ defaultChatServiceSid: myConversationServiceSid });
    
    
    await twilioClient.messages
        .create({ 
          body: text, 
          to: toAddress, 
          from: fromAddress
        })
    

    This means you need to remember to flip it back to the desired service once you conclude development.

    However if you are using Post Event webhooks like onConversationAdded these are fired asynchronously and the conversation service set at the end of the message send flow might not be the one you want.