Cologne_Muc Cologne_Muc - 1 month ago 13
PHP Question

Sendgrid Web API v3 different substitution value for each recipient

I want to send personalized emails with SendGrid. The whole body is similar, it's just 3-4 one-word-substitions in each mail, so I thought of using SendGrid substitutions


  • Bob (bob@example.com) should get an Email saying "Hi Bob, lorem ipsum"

  • Alice (alice@example.com) should get an Email saying "Hi Alice, lorem ipsum"



The environment is a CodeIgniter-Installation using the provided PHP-Class installed by composer.

The problematic function call is addSubstitution($key, $value), which leads to an error 400 (Bad Request). When submitting the requests without this call, everything works as expected (including my placeholders not substituted of course). I'm getting a clean
202
, the emails are arriving.
The error text provided by SendMail is
{"errors":[{"message":"Bad Request","field":null,"help":null}]}
which does not help much.

I thought of adding an array of values to the substitution key. This is copied from this and this code (using the SMTP API in the first example, unclear what in the second), but it seems, that the
value
of
addSubstitution
can only handle strings.

To be clear: I need this functionality in it's generic approach. My problem does not only concern the recipients names in the greeting, also a personalized unsubscribe link etc. I'm adding this hint because an answer like "use the Sendgrid-Marketing-API and upload your recipients in before" does not serve my needs.

My PHP script (light version):

// General

$sg = new \SendGrid('api_key');

$recipients = array(
array(
'email' => 'bob@example.com',
'name' => 'Bob'
),
array(
'email' => 'alice@example.com',
'name' => 'Alice'
)
);

$mail = new \SendGrid\Mail();

$from = new \SendGrid\Email('myname', 'myname@mycompany.com');
$mail->setFrom($from);

$mail->setSubject('New mail');

$content = new \SendGrid\Content('text/plain', 'Hi -name-, lorem ipsum');
$mail->addContent($content);

// Personalizations
$personalization = new \SendGrid\Personalization();

$substitutions_name = array();

foreach ($recipients as $recipient) {
$email = new \SendGrid\Email(null, $recipient['email']);
$personalization->addTo($email);
array_push($substitutions_name, $recipient['name']);
}

$personalization->addSubstitution('-name-', $substitutions_name);

$mail->addPersonalization($personalization);

$response = $sg->client->mail()->send()->post($mail);


Is my approach generally wrong? Is there another similar functionality in SendGrid which serves my needs?

Calling the SMTP-API, which seems to have the needed functionality, is no alternative as I don't want call php mail() in fast and long loops.

Update: As I am digging deeper and deeper my solution should work perfectly. This SO answer has exactly the same approach. But why am I still getting the 400-error? The rest of the code works, as a simple try without the substitution-part shows.

Edit: The resulting JSON of the PHP-Script

{
"from": {
"name": "myname",
"email": "myname@mycompany.com"
},
"personalizations": [
{
"to": [
{
"email": "bob@example.com"
},
{
"email": "alice@example.com"
}
],
"substitutions": {
"-name-": [
"Bob",
"Alice"
]
}
}
],
"subject": "New mail",
"content": [
{
"type": "text/plain",
"value": "Hi -name-, lorem ipsum"
}
]
}


Update: Following bwests answer this is the solution for my problem (tested):

[...]

$content = new \SendGrid\Content('text/plain', 'Hi -name-, lorem ipsum');
$mail->addContent($content);

foreach ($recipients as $recipient) {
$personalization = new \SendGrid\Personalization();
$email = new \SendGrid\Email(null, $recipient['email']);
$personalization->addTo($email);
$personalization->addSubstitution('-name-', $recipient['name']);
$mail->addPersonalization($personalization);
}

$response = $sg->client->mail()->send()->post($mail);

Answer

In v3, substitution values cannot be arrays. Personalizations are different from the legacy SMTP API, though the concepts are the same.

Per this example your payload should look like:

{
  "from": {
    "name": "myname",
    "email": "myname@mycompany.com"
  },
  "personalizations": [
    {
      "to": [
        {
          "email": "alice@example.com"
        }
      ],
      "substitutions": {
        "-name-": "Alice"
      }
    },
    {
      "to": [
        {
          "email": "bob@example.com"
        }
      ],
      "substitutions": {
        "-name-": "Bob"
      }
    }    
  ],
  "subject": "New mail",
  "content": [
    {
      "type": "text/plain",
      "value": "Hi -name-, lorem ipsum"
    }
  ]
}

This change was made to make it easier to look at a single Personalization object and see all of the metadata for that specific email, and to reduce errors that were common due to trying to maintain consistent indices across arrays rather than using structtured data.