List submission events
curl --request GET \
--url https://app.dotportion.com/api/v1/submissions/{id}/events \
--header 'Authorization: Bearer <token>'const options = {method: 'GET', headers: {Authorization: 'Bearer <token>'}};
fetch('https://app.dotportion.com/api/v1/submissions/{id}/events', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));import requests
url = "https://app.dotportion.com/api/v1/submissions/{id}/events"
headers = {"Authorization": "Bearer <token>"}
response = requests.get(url, headers=headers)
print(response.text){
"data": [
{
"seq": 1,
"kind": "attempt_started",
"ts_ms": 1768478400000,
"payload": {}
},
{
"seq": 2,
"kind": "ai_prompt",
"ts_ms": 1768478445000,
"payload": {
"text": "The eviction test is failing. Walk me through what it expects first."
}
},
{
"seq": 3,
"kind": "ai_response",
"ts_ms": 1768478452000,
"payload": {
"tokens": 412,
"model": "sandbox-model"
}
}
],
"meta": {
"request_id": "req_4f2a9c1e8b7d4a5f9e3c2b1a8d7f6e5c",
"next_cursor": "3",
"has_more": true
}
}{
"error": {
"code": "INVALID_INPUT",
"message": "Invalid request",
"details": {
"formErrors": [],
"fieldErrors": {
"name": [
"String must contain at least 1 character(s)"
]
}
},
"request_id": "req_4f2a9c1e8b7d4a5f9e3c2b1a8d7f6e5c"
}
}{
"error": {
"code": "INVALID_TOKEN",
"message": "Invalid or revoked API key",
"request_id": "req_4f2a9c1e8b7d4a5f9e3c2b1a8d7f6e5c"
}
}{
"error": {
"code": "INSUFFICIENT_SCOPE",
"message": "This API key lacks the required scope: write.",
"request_id": "req_4f2a9c1e8b7d4a5f9e3c2b1a8d7f6e5c"
}
}{
"error": {
"code": "NOT_FOUND",
"message": "Resource not found",
"request_id": "req_4f2a9c1e8b7d4a5f9e3c2b1a8d7f6e5c"
}
}{
"error": {
"code": "RATE_LIMITED",
"message": "Rate limit exceeded for write requests (200/hour). Retry after 2026-01-15T13:00:00.000Z.",
"details": {
"limit": 200,
"remaining": 0,
"reset": 1768482000
},
"request_id": "req_4f2a9c1e8b7d4a5f9e3c2b1a8d7f6e5c"
}
}{
"error": {
"code": "INTERNAL_ERROR",
"message": "Internal server error",
"request_id": "req_4f2a9c1e8b7d4a5f9e3c2b1a8d7f6e5c"
}
}Submissions
List submission events
The raw captured timeline: prompts, AI responses, edits, test runs. Large — a busy attempt produces thousands of rows.
Resume a walk by passing the last seq you saw as since_seq. meta.next_cursor is that number.
payload is passed through as captured. Treat it as an evolving blob: read the fields you need defensively rather than depending on its exact shape.
GET
/
api
/
v1
/
submissions
/
{id}
/
events
List submission events
curl --request GET \
--url https://app.dotportion.com/api/v1/submissions/{id}/events \
--header 'Authorization: Bearer <token>'const options = {method: 'GET', headers: {Authorization: 'Bearer <token>'}};
fetch('https://app.dotportion.com/api/v1/submissions/{id}/events', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));import requests
url = "https://app.dotportion.com/api/v1/submissions/{id}/events"
headers = {"Authorization": "Bearer <token>"}
response = requests.get(url, headers=headers)
print(response.text){
"data": [
{
"seq": 1,
"kind": "attempt_started",
"ts_ms": 1768478400000,
"payload": {}
},
{
"seq": 2,
"kind": "ai_prompt",
"ts_ms": 1768478445000,
"payload": {
"text": "The eviction test is failing. Walk me through what it expects first."
}
},
{
"seq": 3,
"kind": "ai_response",
"ts_ms": 1768478452000,
"payload": {
"tokens": 412,
"model": "sandbox-model"
}
}
],
"meta": {
"request_id": "req_4f2a9c1e8b7d4a5f9e3c2b1a8d7f6e5c",
"next_cursor": "3",
"has_more": true
}
}{
"error": {
"code": "INVALID_INPUT",
"message": "Invalid request",
"details": {
"formErrors": [],
"fieldErrors": {
"name": [
"String must contain at least 1 character(s)"
]
}
},
"request_id": "req_4f2a9c1e8b7d4a5f9e3c2b1a8d7f6e5c"
}
}{
"error": {
"code": "INVALID_TOKEN",
"message": "Invalid or revoked API key",
"request_id": "req_4f2a9c1e8b7d4a5f9e3c2b1a8d7f6e5c"
}
}{
"error": {
"code": "INSUFFICIENT_SCOPE",
"message": "This API key lacks the required scope: write.",
"request_id": "req_4f2a9c1e8b7d4a5f9e3c2b1a8d7f6e5c"
}
}{
"error": {
"code": "NOT_FOUND",
"message": "Resource not found",
"request_id": "req_4f2a9c1e8b7d4a5f9e3c2b1a8d7f6e5c"
}
}{
"error": {
"code": "RATE_LIMITED",
"message": "Rate limit exceeded for write requests (200/hour). Retry after 2026-01-15T13:00:00.000Z.",
"details": {
"limit": 200,
"remaining": 0,
"reset": 1768482000
},
"request_id": "req_4f2a9c1e8b7d4a5f9e3c2b1a8d7f6e5c"
}
}{
"error": {
"code": "INTERNAL_ERROR",
"message": "Internal server error",
"request_id": "req_4f2a9c1e8b7d4a5f9e3c2b1a8d7f6e5c"
}
}Authorizations
Your API key, live (sk_live_…) or test (sk_test_…). Create one in Settings → API keys. Send it as Authorization: Bearer <key> — never in a query string.
Path Parameters
The submission id.
Query Parameters
Exclusive lower bound on seq.
Items per page, max 500.
Was this page helpful?