Docs / Surface / contact-integration
Contact Integration
Surface integrates with Contact so you can screen message payloads for threats and trigger Strand workflows based on scan results.
How it works
When a message arrives in Contact and matches a flow with tags, the flow system can trigger Strand workflows that include Surface scan steps. The tag-matching mechanism connects Contact messages to Strand workflows automatically:
- A message arrives at a Contact entity with tags (e.g.
["scan", "incoming"]) - Contact checks for Strand workflows whose trigger tags overlap with the message tags
- Any matching workflow is triggered with the message data as the workflow payload
- If the workflow includes a
surface.platformnode, the payload is scanned
The scan runs as part of the Strand workflow execution; it does not block or delay Contact message delivery.
Tag-matching mechanism
Workflow triggering and connector routing match tags differently, and this is the most common integration mistake:
- Strand workflows fire only when the workflow's trigger tags contain
- Contact connectors use
$inmatching: any single tag overlap triggers
every tag on the message (workflow tags ⊇ message tags). Extra tags on the workflow are fine; a message tag the workflow doesn't have blocks the match.
the connector. Multiple connectors can fire for one message (deduplicated automatically).
For example, a message tagged ["environmental", "data_quality"] does not fire a workflow whose trigger tags are only ["data_quality"], because that workflow is missing environmental. A workflow tagged ["environmental", "data_quality"] does fire for a message tagged ["data_quality"]. Keep message tags minimal and give workflows every tag they should respond to.
Contact trigger payload
When Contact triggers a Strand workflow, the trigger payload has this structure:
{
"accountId": "12345",
"flowId": "flow_abc",
"flowName": "sensor_validation",
"executionId": "exec_xyz",
"tags": ["environmental", "data_quality"],
"entityId": "entities/temperature_sensor_01",
"entityName": "temperature_sensor_01",
"messageId": "6614abcd1234ef5678901234",
"data": {
"temperature": 85.2,
"humidity": 45,
"timestamp": "2025-03-24T10:00:00Z"
},
"timestamp": "2025-03-24T10:00:01Z"
}
Accessing fields in Strand workflows
In Strand workflow nodes, the Contact trigger data is available via the data field in the payload. Because the trigger payload wraps the message data inside a data key, the actual message content is accessed as:
{{ payload.data }}
For example, to scan the message content in a Surface node:
{{ payload.data | tojson }}
To access specific fields from the original message:
{{ payload.data.temperature }}
{{ payload.entity.name }}
{{ payload.tags }}
Available trigger fields
| Field | Type | Description |
|---|---|---|
payload.data |
object | The actual message data payload |
payload.tags |
array | Tags that matched the trigger |
payload.messageId |
string | Contact message ID |
payload.timestamp |
string | ISO 8601 timestamp of the trigger |
payload.entity.id |
string | Entity resource path |
payload.entity.name |
string | Entity display name |
payload.contactFlow.flowId |
string | Contact flow ID that triggered |
payload.contactFlow.flowName |
string | Contact flow name |
payload.contactFlow.executionId |
string | Unique execution ID for this trigger |
meta.account_id |
string | Tendrl account ID |
Setup
Surface scanning in Contact runs through a Strand workflow that contains a Surface scan node. To set it up:
- In Strand, build a workflow with a Surface scan node (see Strand integration) and give the workflow trigger tags (e.g.
["scan"]) - In Contact, tag the messages you want screened, using only tags the workflow also has (the workflow's tags must cover every tag on the message)
- Matching messages automatically trigger the workflow, which scans the payload
No additional API key configuration is required; the platform handles authentication internally. Your account determines the monthly scan limit.
Scan result variables
When a Strand workflow triggered from Contact includes a Surface scan node, the scan result fields are available for downstream conditions:
| Variable | Type | Description |
|---|---|---|
payload.threat_level |
string | Clean, Informational, Suspicious, or Malicious |
payload.score |
integer | 0-100 safety score (higher is safer) |
payload.recommended_action |
string | Allow, Review, or Block |
payload.safe |
boolean | true when threat_level is not in reject_on |
payload.primary_threat |
string | Top matched threat label, or empty string |
payload.request_id |
string | Scan correlation ID |
payload.iocs |
array | Indicators of compromise |
Example branch condition
{{ payload.threat_level == "Malicious" }} -> block or alert
{{ payload.safe == true }} -> forward to downstream connector
Flow execution details
In the Contact flow detail view, each step shows its result, including the Surface scan node's output: threat level, safety score (higher is safer), recommended action, and IOCs.
Limits
Scans in Contact-triggered workflows count against your Surface monthly scan quota. When the quota is exhausted, payloads are forwarded without scanning; no messages are dropped.
Related
- Strand integration: surface in Strand workflows
Tendrl