curl --request POST \
--url https://api.checkfu.com/v1/sessions/{id}/events \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--header 'checkfu-version: <checkfu-version>' \
--data '
{
"type": "user.message",
"payload": {
"content": "<string>",
"authored_by": "<string>",
"caused_by": {
"kind": "api",
"request_id": "<string>"
},
"attachments": [
"<string>"
],
"connected_runtime": "<string>",
"run_delivery": "next_run",
"surface_scope_id": "<string>"
}
}
'import requests
url = "https://api.checkfu.com/v1/sessions/{id}/events"
payload = {
"type": "user.message",
"payload": {
"content": "<string>",
"authored_by": "<string>",
"caused_by": {
"kind": "api",
"request_id": "<string>"
},
"attachments": ["<string>"],
"connected_runtime": "<string>",
"run_delivery": "next_run",
"surface_scope_id": "<string>"
}
}
headers = {
"checkfu-version": "<checkfu-version>",
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {
'checkfu-version': '<checkfu-version>',
Authorization: 'Bearer <token>',
'Content-Type': 'application/json'
},
body: JSON.stringify({
type: 'user.message',
payload: {
content: '<string>',
authored_by: '<string>',
caused_by: {kind: 'api', request_id: '<string>'},
attachments: ['<string>'],
connected_runtime: '<string>',
run_delivery: 'next_run',
surface_scope_id: '<string>'
}
})
};
fetch('https://api.checkfu.com/v1/sessions/{id}/events', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.checkfu.com/v1/sessions/{id}/events",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'type' => 'user.message',
'payload' => [
'content' => '<string>',
'authored_by' => '<string>',
'caused_by' => [
'kind' => 'api',
'request_id' => '<string>'
],
'attachments' => [
'<string>'
],
'connected_runtime' => '<string>',
'run_delivery' => 'next_run',
'surface_scope_id' => '<string>'
]
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json",
"checkfu-version: <checkfu-version>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.checkfu.com/v1/sessions/{id}/events"
payload := strings.NewReader("{\n \"type\": \"user.message\",\n \"payload\": {\n \"content\": \"<string>\",\n \"authored_by\": \"<string>\",\n \"caused_by\": {\n \"kind\": \"api\",\n \"request_id\": \"<string>\"\n },\n \"attachments\": [\n \"<string>\"\n ],\n \"connected_runtime\": \"<string>\",\n \"run_delivery\": \"next_run\",\n \"surface_scope_id\": \"<string>\"\n }\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("checkfu-version", "<checkfu-version>")
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.checkfu.com/v1/sessions/{id}/events")
.header("checkfu-version", "<checkfu-version>")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"type\": \"user.message\",\n \"payload\": {\n \"content\": \"<string>\",\n \"authored_by\": \"<string>\",\n \"caused_by\": {\n \"kind\": \"api\",\n \"request_id\": \"<string>\"\n },\n \"attachments\": [\n \"<string>\"\n ],\n \"connected_runtime\": \"<string>\",\n \"run_delivery\": \"next_run\",\n \"surface_scope_id\": \"<string>\"\n }\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.checkfu.com/v1/sessions/{id}/events")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["checkfu-version"] = '<checkfu-version>'
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"type\": \"user.message\",\n \"payload\": {\n \"content\": \"<string>\",\n \"authored_by\": \"<string>\",\n \"caused_by\": {\n \"kind\": \"api\",\n \"request_id\": \"<string>\"\n },\n \"attachments\": [\n \"<string>\"\n ],\n \"connected_runtime\": \"<string>\",\n \"run_delivery\": \"next_run\",\n \"surface_scope_id\": \"<string>\"\n }\n}"
response = http.request(request)
puts response.read_body{
"id": "<string>",
"seq": 1,
"schema_version": 1,
"created_at": "<string>",
"type": "user.message",
"payload": {
"content": "<string>",
"authored_by": "<string>",
"caused_by": {
"kind": "api",
"request_id": "<string>"
},
"attachments": [
"<string>"
],
"prompt_integrity": {
"analyzer_version": "unicode-17.0.0-uts39-r32-checkfu-1",
"disposition": "accepted_normalization",
"original_digest": "<string>",
"findings": [
{
"kind": "mixed_script_confusable",
"utf16_offset": 1,
"code_point": "<string>",
"replacement": "<string>",
"answer_id": "<string>",
"value_index": 1
}
]
},
"connected_runtime": "<string>",
"run_delivery": "next_run",
"host_context": {
"source": "<string>",
"data": {}
},
"surface_scope_id": "<string>"
},
"attested": true
}{
"error": {
"type": "validation.malformed",
"message": "<string>",
"more": "https://docs.checkfu.com/reference/errors#validation-malformed"
}
}{
"error": {
"type": "auth.invalid_key",
"message": "<string>",
"more": "https://docs.checkfu.com/reference/errors#auth-invalid-key"
}
}{
"error": {
"type": "auth.disabled_tenancy",
"message": "<string>",
"more": "https://docs.checkfu.com/reference/errors#auth-disabled-tenancy"
}
}{
"error": {
"type": "validation.not_found",
"message": "<string>",
"more": "https://docs.checkfu.com/reference/errors#validation-not-found"
}
}{
"error": {
"type": "validation.conflict",
"message": "<string>",
"more": "https://docs.checkfu.com/reference/errors#validation-conflict"
}
}{
"error": {
"type": "model.no_eligible_model",
"message": "<string>",
"more": "https://docs.checkfu.com/reference/errors#model-no-eligible-model"
}
}{
"error": {
"type": "budget.exceeded",
"message": "<string>",
"more": "https://docs.checkfu.com/reference/errors#budget-exceeded"
}
}{
"error": {
"type": "runtime.internal",
"message": "<string>",
"more": "https://docs.checkfu.com/reference/errors#runtime-internal"
}
}{
"error": {
"type": "runtime.runner_unavailable",
"message": "<string>",
"more": "https://docs.checkfu.com/reference/errors#runtime-runner-unavailable"
}
}Drive a Session with a client event
Appends one client event to the Session, which is how work is made to happen: user.message starts a turn or steers a running one, user.interrupt ends the current turn without making the Session terminal, user.custom_tool_result returns the result of a parked Custom tool by its tool_use_id, user.question_answer answers one active agent.question, user.define_outcome attaches a rubric-graded definition of done, and system.message appends privileged system context that applies to the accompanying turn and every later one. System context is governed: it is control-plane authority, so a runner credential is refused before the Session is even looked up; it is refused when the Session’s admitted model route cannot carry a mid-conversation system turn (an Anthropic route takes system as a top-level parameter only, so there is no position for one); it never replaces the agent definition’s system prompt, which stays frozen; and a Session carries at most 16 of them, because unlike conversation this context is never condensed away and something has to bound it. It appends without starting a turn — a Run already in flight keeps its frozen spec, and the next one reads the instruction. A user.message content is a plain string or an array of typed content blocks (the CMA shape, adopted as-is): text blocks, image blocks with base64/url/file sources (png/jpeg/gif/webp), and document blocks with base64 (PDF)/text/url/file sources plus optional title and context. Content blocks are model-visible input, never a second mount system. Inline images, PDFs, and text documents render at the trusted model gateway in the exact provider dialect and typed user history survives later Runs. A message carries at most 32 blocks and inline sources are byte-capped per block and per message (1 MiB). url sources are fetched platform-side only where the operator has configured a content-fetch allowlist: https only, to an allowlisted public DNS host, carrying no userinfo and no explicit port, following no redirect, and refused with a named reason where a deployment configures none; file sources are admitted by snapshotting the referenced File’s bytes into the message’s own inline content at admission (plan 755), so the stored event binds to the snapshot — deleting the original File never affects the stored event, its later-Turn replay, or the stream — and a File whose inline snapshot exceeds the byte caps is refused with a named reason. A new idempotency receipt binds the pre-snapshot authored event to the first admitted snapshot: an exact Idempotency-Key retry returns that stored event before a second File read or URL fetch, including after the original File is deleted, while changing the authored source under the same key conflicts before source access. Receipts written before this version retain compatibility by rebuilding the admitted event before their legacy comparison. A ZDR Workspace refuses block content entirely, exactly as it refuses attachments; and a model wire dialect that cannot carry a block type refuses at admission as runtime.harness_incompatible, never as a provider-shaped runtime error. A block-carrying message sent while a Run is active always begins the next Run rather than being flattened into the native mid-run channel. Every payload carries authored_by and caused_by attribution; an Idempotency-Key retry returns the first stored event instead of starting a second turn. The response is the stored event envelope — read settlement from the ledger or the stream, never from response timing. A command sent in the wrong state, or answering the wrong tool call or Question, is refused as an invalid transition without advancing the Session.
Checkfu support posture: alpha; hosted. Required evidence journey: session-turn. Deployment-specific readiness and the latest proven release are available from GET /v1/support/capabilities.
curl --request POST \
--url https://api.checkfu.com/v1/sessions/{id}/events \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--header 'checkfu-version: <checkfu-version>' \
--data '
{
"type": "user.message",
"payload": {
"content": "<string>",
"authored_by": "<string>",
"caused_by": {
"kind": "api",
"request_id": "<string>"
},
"attachments": [
"<string>"
],
"connected_runtime": "<string>",
"run_delivery": "next_run",
"surface_scope_id": "<string>"
}
}
'import requests
url = "https://api.checkfu.com/v1/sessions/{id}/events"
payload = {
"type": "user.message",
"payload": {
"content": "<string>",
"authored_by": "<string>",
"caused_by": {
"kind": "api",
"request_id": "<string>"
},
"attachments": ["<string>"],
"connected_runtime": "<string>",
"run_delivery": "next_run",
"surface_scope_id": "<string>"
}
}
headers = {
"checkfu-version": "<checkfu-version>",
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {
'checkfu-version': '<checkfu-version>',
Authorization: 'Bearer <token>',
'Content-Type': 'application/json'
},
body: JSON.stringify({
type: 'user.message',
payload: {
content: '<string>',
authored_by: '<string>',
caused_by: {kind: 'api', request_id: '<string>'},
attachments: ['<string>'],
connected_runtime: '<string>',
run_delivery: 'next_run',
surface_scope_id: '<string>'
}
})
};
fetch('https://api.checkfu.com/v1/sessions/{id}/events', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.checkfu.com/v1/sessions/{id}/events",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'type' => 'user.message',
'payload' => [
'content' => '<string>',
'authored_by' => '<string>',
'caused_by' => [
'kind' => 'api',
'request_id' => '<string>'
],
'attachments' => [
'<string>'
],
'connected_runtime' => '<string>',
'run_delivery' => 'next_run',
'surface_scope_id' => '<string>'
]
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json",
"checkfu-version: <checkfu-version>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.checkfu.com/v1/sessions/{id}/events"
payload := strings.NewReader("{\n \"type\": \"user.message\",\n \"payload\": {\n \"content\": \"<string>\",\n \"authored_by\": \"<string>\",\n \"caused_by\": {\n \"kind\": \"api\",\n \"request_id\": \"<string>\"\n },\n \"attachments\": [\n \"<string>\"\n ],\n \"connected_runtime\": \"<string>\",\n \"run_delivery\": \"next_run\",\n \"surface_scope_id\": \"<string>\"\n }\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("checkfu-version", "<checkfu-version>")
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.checkfu.com/v1/sessions/{id}/events")
.header("checkfu-version", "<checkfu-version>")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"type\": \"user.message\",\n \"payload\": {\n \"content\": \"<string>\",\n \"authored_by\": \"<string>\",\n \"caused_by\": {\n \"kind\": \"api\",\n \"request_id\": \"<string>\"\n },\n \"attachments\": [\n \"<string>\"\n ],\n \"connected_runtime\": \"<string>\",\n \"run_delivery\": \"next_run\",\n \"surface_scope_id\": \"<string>\"\n }\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.checkfu.com/v1/sessions/{id}/events")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["checkfu-version"] = '<checkfu-version>'
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"type\": \"user.message\",\n \"payload\": {\n \"content\": \"<string>\",\n \"authored_by\": \"<string>\",\n \"caused_by\": {\n \"kind\": \"api\",\n \"request_id\": \"<string>\"\n },\n \"attachments\": [\n \"<string>\"\n ],\n \"connected_runtime\": \"<string>\",\n \"run_delivery\": \"next_run\",\n \"surface_scope_id\": \"<string>\"\n }\n}"
response = http.request(request)
puts response.read_body{
"id": "<string>",
"seq": 1,
"schema_version": 1,
"created_at": "<string>",
"type": "user.message",
"payload": {
"content": "<string>",
"authored_by": "<string>",
"caused_by": {
"kind": "api",
"request_id": "<string>"
},
"attachments": [
"<string>"
],
"prompt_integrity": {
"analyzer_version": "unicode-17.0.0-uts39-r32-checkfu-1",
"disposition": "accepted_normalization",
"original_digest": "<string>",
"findings": [
{
"kind": "mixed_script_confusable",
"utf16_offset": 1,
"code_point": "<string>",
"replacement": "<string>",
"answer_id": "<string>",
"value_index": 1
}
]
},
"connected_runtime": "<string>",
"run_delivery": "next_run",
"host_context": {
"source": "<string>",
"data": {}
},
"surface_scope_id": "<string>"
},
"attested": true
}{
"error": {
"type": "validation.malformed",
"message": "<string>",
"more": "https://docs.checkfu.com/reference/errors#validation-malformed"
}
}{
"error": {
"type": "auth.invalid_key",
"message": "<string>",
"more": "https://docs.checkfu.com/reference/errors#auth-invalid-key"
}
}{
"error": {
"type": "auth.disabled_tenancy",
"message": "<string>",
"more": "https://docs.checkfu.com/reference/errors#auth-disabled-tenancy"
}
}{
"error": {
"type": "validation.not_found",
"message": "<string>",
"more": "https://docs.checkfu.com/reference/errors#validation-not-found"
}
}{
"error": {
"type": "validation.conflict",
"message": "<string>",
"more": "https://docs.checkfu.com/reference/errors#validation-conflict"
}
}{
"error": {
"type": "model.no_eligible_model",
"message": "<string>",
"more": "https://docs.checkfu.com/reference/errors#model-no-eligible-model"
}
}{
"error": {
"type": "budget.exceeded",
"message": "<string>",
"more": "https://docs.checkfu.com/reference/errors#budget-exceeded"
}
}{
"error": {
"type": "runtime.internal",
"message": "<string>",
"more": "https://docs.checkfu.com/reference/errors#runtime-internal"
}
}{
"error": {
"type": "runtime.runner_unavailable",
"message": "<string>",
"more": "https://docs.checkfu.com/reference/errors#runtime-runner-unavailable"
}
}Authorizations
Bearer authentication header of the form Bearer <token>, where <token> is your auth token.
Headers
2026-08-24 255Path Parameters
^sess_[0-9a-f]{32}$Body
Response
Success
- Option 1
- Option 2
- Option 3
- Option 4
- Option 5
- Option 6
- Option 7
- Option 8
- Option 9
- Option 10
- Option 11
- Option 12
- Option 13
- Option 14
- Option 15
- Option 16
- Option 17
- Option 18
- Option 19
- Option 20
- Option 21
- Option 22
- Option 23
- Option 24
- Option 25
- Option 26
- Option 27
- Option 28
- Option 29
- Option 30
- Option 31
- Option 32
- Option 33
- Option 34
- Option 35
- Option 36
- Option 37
- Option 38
- Option 39
- Option 40
- Option 41
- Option 42
- Option 43
- Option 44
- Option 45
- Option 46
- Option 47
- Option 48
- Option 49
- Option 50
- Option 51
- Option 52
- Option 53
- Option 54
- Option 55
- Option 56
- Option 57
- Option 58
- Option 59
- Option 60
- Option 61
- Option 62
- Option 63
- Option 64
- Option 65
- Option 66
- Option 67
- Option 68
- Option 69
- Option 70
- Option 71
- Option 72
- Option 73
- Option 74
- Option 75
- Option 76
- Option 77
- Option 78
- Option 79
- Option 80
- Option 81
- Option 82
- Option 83
- Option 84
- Option 85
- Option 86
- Option 87
- Option 88
- Option 89
- Option 90
- Option 91
- Option 92
- Option 93
- Option 94
- Option 95
- Option 96
- Option 97
- Option 98
- Option 99
- Option 100
- Option 101
- Option 102
- Option 103
- Option 104
- Option 105
- Option 106
- Option 107
- Option 108
- Option 109
- Option 110
- Option 111
- Option 112
- Option 113
- Option 114
- Option 115
- Option 116
- Option 117
- Option 118
- Option 119
- Option 120
- Option 121
- Option 122
- Option 123
- Option 124
- Option 125
- Option 126
- Option 127
- Option 128
- Option 129
- Option 130
^evt_[0-9a-f]{32}$x > 01, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42 user.message Show child attributes
Show child attributes
true