Why Use an API Trigger?
Most customer feedback is best requested after a real event: an order is delivered, a support case is resolved, a trial reaches day seven, a service appointment ends, or a customer finishes onboarding. API triggers let your backend queue that survey without building an invitation system yourself.
The integration can stay small. Your system sends a recipient, optional timing, and optional metadata. Survey.is creates one tracked invitation, sends it through the survey invitation worker, and connects the eventual response back to that invitation.
Setup
- Create the survey and make it Active when you are ready to receive responses.
- Open Invitations, then the API trigger tab.
- Create a trigger token, copy the secret immediately, and store it in your backend environment.
- Set the default subject, message, reminder days, and token cooldown in Survey.is.
- Call the trigger endpoint from your server when the customer event happens.
The API trigger tab inside Survey.is shows the endpoint and a copyable cURL example for the active token. For every supported request field, keep the API docs open while you wire the first integration.
Do not call API triggers from browser JavaScript or mobile clients. The trigger secret can queue invitations, so keep it on a trusted server.
Payload
The smallest request needs one recipient email. Add a name for friendlier email templates, send_in_seconds for a delay, and metadata for stable identifiers you want available later in Results.
{
"email": "customer@example.com",
"name": "Example Customer",
"send_in_seconds": 259200,
"metadata": {
"order_id": "ord_1042",
"fulfillment_event": "delivered"
}
}
This obfuscated log shows a successful delayed invitation request and response:
Mon May 18 09:42:11 EDT 2026 [1024] - SENDING SURVEY TO: customer@example.com
Mon May 18 09:42:11 EDT 2026 [1024] - Send survey endpoint: https://survey.is/api/invitation-trigger
Mon May 18 09:42:11 EDT 2026 [1024] - Sending Survey.is payload:
Mon May 18 09:42:11 EDT 2026 [1024] - {"email":"customer@example.com","name":"Example Customer","send_in_seconds":259200,"metadata":{"order_id":"ord_1042","fulfillment_event":"delivered"}}
Mon May 18 09:42:11 EDT 2026 [1024] - RECEIVED SURVEY.IS RESPONSE:
Mon May 18 09:42:11 EDT 2026 [1024] - HTTP Code: 200
Mon May 18 09:42:11 EDT 2026 [1024] - {"ok":true,"message":"Invitation queued.","batch":{"uuid":"4b95d20a-bb8f-4c42-b894-d9fd83303ec2","scheduled_at":"2026-05-21 13:42:11","status":"scheduled"}}
Keep metadata useful and boring: source event IDs, order IDs, customer segments, or workflow names. Do not send passwords, payment card data, raw API secrets, or sensitive regulated information.
Examples
Each example sends the same delayed invitation. Use the language your backend already uses, and keep SURVEYIS_TRIGGER_TOKEN in server-side environment configuration.
<?php
function queueSurveyInvitation($email, $name = '', array $metadata = array(), $delaySeconds = 0)
{
$endpoint = 'https://survey.is/api/invitation-trigger';
// Keep this token server-side. Anyone with it can queue invitations.
$token = getenv('SURVEYIS_TRIGGER_TOKEN');
if (!$token) {
throw new RuntimeException('Missing Survey.is trigger token.');
}
$payload = array(
'email' => $email,
'name' => $name,
'metadata' => $metadata,
);
// Optional: wait before Survey.is sends the invitation email.
if ((int) $delaySeconds > 0) {
$payload['send_in_seconds'] = (int) $delaySeconds;
}
$ch = curl_init($endpoint);
curl_setopt_array($ch, array(
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => json_encode($payload),
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => array(
'Authorization: Bearer ' . $token,
'Content-Type: application/json',
),
));
$body = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$error = curl_error($ch);
curl_close($ch);
if ($error || $httpCode < 200 || $httpCode >= 300) {
throw new RuntimeException('Survey.is trigger failed: ' . ($error ?: $body));
}
return json_decode($body, true);
}
$result = queueSurveyInvitation(
'customer@example.com',
'Example Customer',
// Metadata appears in Results after this recipient submits a response.
array('order_id' => 'ord_1042', 'fulfillment_event' => 'delivered'),
3 * 24 * 60 * 60
);
error_log('Queued Survey.is batch ' . $result['batch']['uuid']);
# Reads the trigger token from the server environment.
curl -sS -X POST 'https://survey.is/api/invitation-trigger' \
-H "Authorization: Bearer $SURVEYIS_TRIGGER_TOKEN" \
-H 'Content-Type: application/json' \
-d '{
"email": "customer@example.com",
"name": "Example Customer",
"send_in_seconds": 259200,
"metadata": {
"order_id": "ord_1042",
"fulfillment_event": "delivered"
}
}'
import os
import requests
endpoint = "https://survey.is/api/invitation-trigger"
token = os.environ["SURVEYIS_TRIGGER_TOKEN"]
payload = {
"email": "customer@example.com",
"name": "Example Customer",
# Delay three days before Survey.is sends the email.
"send_in_seconds": 3 * 24 * 60 * 60,
# Metadata is hidden from the respondent and available in Results.
"metadata": {
"order_id": "ord_1042",
"fulfillment_event": "delivered",
},
}
response = requests.post(
endpoint,
headers={"Authorization": f"Bearer {token}"},
json=payload,
timeout=30,
)
if not response.ok or not response.json().get("ok"):
raise RuntimeError(f"Survey.is trigger failed: {response.status_code} {response.text}")
print("Queued Survey.is batch", response.json()["batch"]["uuid"])
const endpoint = "https://survey.is/api/invitation-trigger";
const token = process.env.SURVEYIS_TRIGGER_TOKEN;
const payload = {
email: "customer@example.com",
name: "Example Customer",
// Delay three days before Survey.is sends the email.
send_in_seconds: 3 * 24 * 60 * 60,
// Metadata is hidden from the respondent and available in Results.
metadata: {
order_id: "ord_1042",
fulfillment_event: "delivered"
}
};
const response = await fetch(endpoint, {
method: "POST",
headers: {
Authorization: `Bearer ${token}`,
"Content-Type": "application/json"
},
body: JSON.stringify(payload)
});
const data = await response.json();
if (!response.ok || !data.ok) {
throw new Error(`Survey.is trigger failed: ${response.status} ${JSON.stringify(data)}`);
}
console.log(`Queued Survey.is batch ${data.batch.uuid}`);
package main
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
"os"
)
func main() {
payload := map[string]interface{}{
"email": "customer@example.com",
"name": "Example Customer",
// Delay three days before Survey.is sends the email.
"send_in_seconds": 3 * 24 * 60 * 60,
// Metadata is hidden from the respondent and available in Results.
"metadata": map[string]string{
"order_id": "ord_1042",
"fulfillment_event": "delivered",
},
}
body, _ := json.Marshal(payload)
req, err := http.NewRequest("POST", "https://survey.is/api/invitation-trigger", bytes.NewReader(body))
if err != nil {
panic(err)
}
req.Header.Set("Authorization", "Bearer "+os.Getenv("SURVEYIS_TRIGGER_TOKEN"))
req.Header.Set("Content-Type", "application/json")
resp, err := http.DefaultClient.Do(req)
if err != nil {
panic(err)
}
defer resp.Body.Close()
responseBody, _ := io.ReadAll(resp.Body)
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
panic(fmt.Sprintf("Survey.is trigger failed: %d %s", resp.StatusCode, responseBody))
}
fmt.Printf("Survey.is response: %s\n", responseBody)
}
Responses and Retries
Successful requests return HTTP 200 with a batch summary. The batch is scheduled immediately or at the time calculated from send_in_seconds or send_at.
{
"ok": true,
"message": "Invitation queued.",
"batch": {
"uuid": "4b95d20a-bb8f-4c42-b894-d9fd83303ec2",
"scheduled_at": "2026-05-21 13:42:11",
"status": "scheduled"
}
}
For network errors and 5xx responses, retry with normal backoff from your job queue. For HTTP 429, wait for the trigger token cooldown. For HTTP 400, fix the payload, token, survey state, or workspace plan before retrying.
Implementation Checklist
- Store the trigger token in backend environment configuration, not in client code.
- Keep the survey Active before expecting queued invitations to send.
- Use one timing field per request: send_in_seconds or send_at.
- Include a stable event or order identifier in metadata so results can be traced later.
- Log request outcomes, but do not log bearer tokens or personal survey invitation links.
- Make your own backend event handling idempotent so retries do not create duplicate invitations.
For full field reference and multi-recipient payloads, use the API docs.