Integrate/Harness API
Harness API
External harnesses integrate through /api/harness/* on api.tryether.ai. This is separate from legacy internal os-api routes.
OpenAPI source
ether-core/docs/openapi/harness-v1.yaml
Mental model
Claim gives you everything needed to run one attempt:
- Prompt, definition of done, repo/branch scope
- Context document IDs (MD corpus)
- Callback URLs for heartbeat, steps, interrupt, result
- WebSocket topic for live UI
Return through those callbacks so Ether can update the task, memory, and user-facing delivery.
Routes
| Method | Path | Purpose |
|---|---|---|
GET | /api/harness/tasks/{task_id}/execution | Preview bundle without claiming |
POST | /api/harness/tasks/{task_id}/claim | Acquire lease → ExecutionBundle |
POST | /api/harness/tasks/{task_id}/heartbeat | Extend lease |
POST | /api/harness/tasks/{task_id}/steps | User-visible progress |
POST | /api/harness/tasks/{task_id}/interrupt | Pause task; ask user a question |
POST | /api/harness/tasks/{task_id}/result | Final outcome |
GET | /api/harness/context?task_id=&slices= | Context slice metadata |
GET | /api/harness/context/doc/{doc_id}?task_id= | Markdown document body |
GET | /api/harness/tools | Tool manifest |
GET | /api/harness/skills?domain= | Skill manifest |
Claim request
json
{
"app_id": "my-harness"
}Response includes run_id, lease_expires_at, and bundle with callbacks:
json
{
"callbacks": {
"report_step_url": "https://api.tryether.ai/api/harness/tasks/task_abc/steps",
"heartbeat_url": "…/heartbeat",
"interrupt_url": "…/interrupt",
"result_url": "…/result",
"websocket_topic": "tasks/task_abc"
}
}Production rule
Use these URLs from the bundle — do not hardcode paths in production.
Execution result
json
{
"run_id": "run_…",
"status": "submitted",
"agent_status": "Opened PR #42",
"artifact": {
"pr": {
"number": 42,
"branch": "ether/fix",
"url": "https://github.com/org/repo/pull/42"
},
"agent_status": "Opened PR #42"
},
"working_summary": "Optional notes for memory compaction"
}Status values
| Status | Ether behavior |
|---|---|
submitted | Task → Completed; task.delivered published |
blocked | Task → Paused; question surfaced to user |
no_changes | Progress updated; harness should explain via no_changes_reason |
During execution
| Call | When |
|---|---|
| heartbeat | Periodically while long runs execute |
| steps | User-facing narration (“Pulling context…”, “Opening PR…”) |
| interrupt | Need user input mid-run (alternative to blocked result) |
Steps emit os.harness.step on the task WebSocket topic.
Context documents
| Doc | Content |
|---|---|
profile.md | Owner preferences |
project.md | Stack, conventions |
repo.md | Repository notes |
task/{task_id}.md | Active goal |
working/{run_id}.md | Written back when you send working_summary |
Conflicts
409 on claim — another harness holds an active lease with a different app_id. Same app can reclaim its run.
Division of responsibility
| Ether | Harness |
|---|---|
| Task interpretation & delivery | Model + tool loop |
| Context corpus & compaction | Git checkout, file edits |
| Lease & timeline | PR creation, tests |
| Judge + deliverable (on submit) | Escalation policy |