Placeholders in outbound calls - Terminal 3 Documentation
Documentation Index
Fetch the complete documentation index at: /llms.txt
Use this file to discover all available pages before exploring further.
When your contract needs to send private data (e.g., PII — name, date of birth, email, etc.) to a third-party API, it does not read the values and inline them. Instead it uses the http-with-placeholders host interface: you put {{profile.<field>}} markers in the request, and the host resolves them from the calling user’s profile inside the enclave, just before the request goes out. The plaintext never enters your WASM.
Agent → z:<tid>:contract → host (http-with-placeholders) → Duffel
book-offer templates {{profile.*}} resolves the markers from POST /orders
into the order body the calling user's profile, (real PII)
then sends the rendered request
{ id, pnr } ◀─────────────────────────────────────────────────────────────── { id, pnr }
Your contract in Rust:
use crate::bindings::t3n::host::http_with_placeholders as hwp;
// The {{profile.<path>}} markers are resolved host-side from the calling
// user's profile — this contract never sees the plaintext values.
let body = serde_json::json!({
"data": {
"type": "instant",
"selected_offers": [req.offer_id],
"passengers": [{\
"id": "passenger_0",\
"given_name": "{{profile.first_name}}",\
"family_name": "{{profile.last_name}}",\
"born_on": "{{profile.date_of_birth}}",\
"email": "{{profile.verified_contacts.email.value}}",\
}]
}
});
let resp = hwp::call(&hwp::Request {
method: "POST".to_string(),
url: "https://api.duffel.com/air/orders".to_string(),
headers: vec![\
("Authorization".to_string(), format!("Bearer {api_key}")),\
("Duffel-Version".to_string(), "v2".to_string()),\
("Content-Type".to_string(), "application/json".to_string()),\
],
body: Some(serde_json::to_vec(&body)?),
})?;
// resp.body — Duffel's response (booking id + PNR). The passport/name/DOB
// were substituted by the host; your WASM never held them.
Key points:
- Synchronous. Like plain
http, you get the upstream response back in the same invocation — there’s no deferred queue. - Profile access is gated by the user’s delegation. The markers resolve only when the calling agent is authorized to act for that user. A marker your contract isn’t permitted to resolve fails with
placeholder not permitted: <marker>. - Egress is the same rule as
http. The target host must be on the user’s allowed-hosts grant, or the call is denied (host/http.egress_denied). - Markers reference the user profile schema — e.g.
{{profile.first_name}},{{profile.date_of_birth}},{{profile.verified_contacts.email.value}}. Fields the schema doesn’t carry yet (passport, title) are supplied by your contract directly.