Touchpoints · Best for CRM integrations
Trigger a touchpoint
POST/v1/touchpoint-events
Send the moment, not the workflow. Video Intros finds the organization's touchpoint and from its configuration decides the script, the page and what happens next. Your integration does not choose a template or write instructions.
Authentication
A bearer API key. Authentication.
Headers
AuthorizationstringrequiredBearer followed by your API key. A test key works in the test environment only.
Idempotency-KeystringrequiredA value that is the same every time you send THIS event, and different for every other event.
Content-Typestringrequiredapplication/json
Request body
touchpointstringrequiredThe slug of one of the organization's touchpoints, from GET /v1/touchpoints.
occurred_atstringrequiredWhen the moment happened in your system. ISO 8601.
memberobjectrequiredThe person on your team who records the video. Identify them by any one of these; Video Intros resolves the member in the organization.
3 fields in
memberidstringThe member's Video Intros identifier, from GET /v1/members.
external_idstringYour own identifier for this person, as stored on the member.
emailstringThe member's email address. If more than one member has it, the request is refused with member_ambiguous.
recipientobjectrequiredThe customer the Video Intro is for.
3 fields in
recipientfirst_namestringrequiredUsed in the script and on the page.
last_namestringOptional.
phonestringrequiredMobile number in E.164 form, for example +12405550100.
referenceobjectThe record in your system this is about. Stored with the Video Intro and returned on every event, so you can match it back.
3 fields in
referencesourcestringThe system the record lives in, for example your_crm.
typestringWhat kind of record: appointment, job, deal, contact.
idstringIts identifier in that system.
contextobjectFacts about the moment that the touchpoint can use.
3 fields in
contextappointmentobjectWhen the moment has a date and time.
2 fields in
appointmentstarts_atstringISO 8601 date and time.
timezonestringIANA time zone, for example America/New_York.
tradestringExterior remodeling: roofing, siding, windows, doors or gutters.
lanestringExterior remodeling: retail or insurance.
metadataobjectUp to a few of your own key and value pairs. Stored and returned, never interpreted.
Errors
| Status | Code | When |
|---|---|---|
401 | unauthorized | The API key is missing, malformed or revoked. |
409 | idempotency_conflict | This Idempotency-Key was already used with a different body. |
422 | validation_failed | A required field is missing or malformed. The response names the field. |
422 | touchpoint_not_found | The organization has no touchpoint with that slug, or it is turned off. |
422 | member_not_found | No active member matches the member you named. |
422 | member_ambiguous | More than one member has that email address. Send the member's id or external_id instead. |
429 | rate_limited | Too many requests. Wait for the time in Retry-After and send again with the same Idempotency-Key. |
curl -X POST https://api.videointros.com/v1/touchpoint-events \
-H "Authorization: Bearer $VIDEO_INTROS_API_KEY" \
-H "Idempotency-Key: your_crm:appointment_123:new_appointment" \
-H "Content-Type: application/json" \
-d '{
"touchpoint": "new_appointment",
"occurred_at": "2026-10-05T17:40:00Z",
"member": {
"external_id": "rep_456"
},
"recipient": {
"first_name": "Andrea",
"last_name": "Smith",
"phone": "+12405550100"
},
"reference": {
"source": "your_crm",
"type": "appointment",
"id": "appointment_123"
},
"context": {
"appointment": {
"starts_at": "2026-10-06T18:00:00Z",
"timezone": "America/New_York"
}
},
"metadata": {
"job_id": "job_789"
}
}'
const response = await fetch("https://api.videointros.com/v1/touchpoint-events", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.VIDEO_INTROS_API_KEY}`,
"Idempotency-Key": "your_crm:appointment_123:new_appointment",
"Content-Type": "application/json",
},
body: JSON.stringify({
"touchpoint": "new_appointment",
"occurred_at": "2026-10-05T17:40:00Z",
"member": {
"external_id": "rep_456"
},
"recipient": {
"first_name": "Andrea",
"last_name": "Smith",
"phone": "+12405550100"
},
"reference": {
"source": "your_crm",
"type": "appointment",
"id": "appointment_123"
},
"context": {
"appointment": {
"starts_at": "2026-10-06T18:00:00Z",
"timezone": "America/New_York"
}
},
"metadata": {
"job_id": "job_789"
}
}),
});
const result = await response.json();
import os, requests
response = requests.post(
"https://api.videointros.com/v1/touchpoint-events",
headers={
"Authorization": f"Bearer {os.environ['VIDEO_INTROS_API_KEY']}",
"Idempotency-Key": "your_crm:appointment_123:new_appointment",
},
json={
"touchpoint": "new_appointment",
"occurred_at": "2026-10-05T17:40:00Z",
"member": {
"external_id": "rep_456",
},
"recipient": {
"first_name": "Andrea",
"last_name": "Smith",
"phone": "+12405550100",
},
"reference": {
"source": "your_crm",
"type": "appointment",
"id": "appointment_123",
},
"context": {
"appointment": {
"starts_at": "2026-10-06T18:00:00Z",
"timezone": "America/New_York",
},
},
"metadata": {
"job_id": "job_789",
},
},
)
result = response.json()
{
"id": "evt_8f3c2a71d04e",
"object": "touchpoint_event",
"status": "accepted",
"touchpoint": "new_appointment",
"environment": "test",
"created_at": "2026-10-05T17:40:01Z"
}
{
"error": {
"code": "idempotency_conflict",
"message": "This Idempotency-Key was already used with a different body."
}
}