This page walks through common end-to-end workflows with the Devin API. Each flow includes the full sequence of API calls with code examples. For individual endpoint details, see the relevant API reference pages.
Setup
Set these environment variables before running any example:
# Required: your service user API key (starts with cog_)
export DEVIN_API_KEY="cog_your_key_here"
# Required: your organization ID (find it on Settings > Service Users)
export DEVIN_ORG_ID="your_org_id"
Getting started: API key to first session
The most common workflow — authenticate, discover your account, and create your first session.
Step 1: Verify your credentials
Organization-scoped service users can skip to Step 3 — you already know your org ID from the Settings page.
curl "https://api.devin.ai/v3/self" \
-H "Authorization: Bearer $DEVIN_API_KEY"
Response:
{
"principal_type": "service_user",
"service_user_id": "service-user-abc123",
"service_user_name": "CI Bot",
"org_id": null
}
Step 2: List your organizations
Enterprise service users can list all organizations:
curl "https://api.devin.ai/v3/enterprise/organizations" \
-H "Authorization: Bearer $DEVIN_API_KEY"
Organization-scoped service users already know their org ID (it’s on the Settings > Service Users page where the key was created).
Step 3: Create a session
curl -X POST "https://api.devin.ai/v3/organizations/$DEVIN_ORG_ID/sessions" \
-H "Authorization: Bearer $DEVIN_API_KEY" \
-H "Content-Type: application/json" \
-d '{"prompt": "Create a Python script that analyzes CSV data"}'
Response:
{
"session_id": "devin-abc123",
"url": "https://app.devin.ai/sessions/devin-abc123",
"status": "running"
}
Step 4: Poll for events
Monitor the session by polling for messages:
curl "https://api.devin.ai/v3/organizations/$DEVIN_ORG_ID/sessions/devin-abc123/messages" \
-H "Authorization: Bearer $DEVIN_API_KEY"
Full Python example
import os
import time
import requests
API_KEY = os.environ["DEVIN_API_KEY"]
ORG_ID = os.environ["DEVIN_ORG_ID"]
BASE = "https://api.devin.ai/v3"
HEADERS = {"Authorization": f"Bearer {API_KEY}"}
# 1. Verify credentials
me = requests.get(f"{BASE}/self", headers=HEADERS)
me.raise_for_status()
data = me.json()
print(f"Authenticated as: {data.get('service_user_name') or data.get('user_name')}")
# 2. Create a session
session = requests.post(
f"{BASE}/organizations/{ORG_ID}/sessions",
headers={**HEADERS, "Content-Type": "application/json"},
json={"prompt": "Create a Python script that analyzes CSV data"}
).json()
print(f"Session: {session['url']}")
# 3. Poll until complete
while True:
status = requests.get(
f"{BASE}/organizations/{ORG_ID}/sessions/{session['session_id']}",
headers=HEADERS
).json()["status"]
print(f"Status: {status}")
if status in ("exit", "error", "suspended"):
break
time.sleep(10)
Downloading session attachments
Retrieve files produced by a session (logs, screenshots, generated code, etc.).
Step 1: Get the session
curl "https://api.devin.ai/v3/organizations/$DEVIN_ORG_ID/sessions/$SESSION_ID" \
-H "Authorization: Bearer $DEVIN_API_KEY"
Step 2: List attachments
curl "https://api.devin.ai/v3/organizations/$DEVIN_ORG_ID/sessions/$SESSION_ID/attachments" \
-H "Authorization: Bearer $DEVIN_API_KEY"
Response:
{
"items": [
{
"attachment_id": "att_123",
"name": "output.py",
"url": "https://..."
}
]
}
Step 3: Download
Use the url from the attachment response to download the file directly.
Full Python example
import os
import requests
API_KEY = os.environ["DEVIN_API_KEY"]
ORG_ID = os.environ["DEVIN_ORG_ID"]
SESSION_ID = os.environ["SESSION_ID"]
BASE = "https://api.devin.ai/v3"
HEADERS = {"Authorization": f"Bearer {API_KEY}"}
# List attachments
attachments = requests.get(
f"{BASE}/organizations/{ORG_ID}/sessions/{SESSION_ID}/attachments",
headers=HEADERS
).json()["items"]
# Download each attachment
for att in attachments:
print(f"Downloading {att['name']}...")
content = requests.get(att["url"]).content
with open(att["name"], "wb") as f:
f.write(content)
print(f" Saved {att['name']} ({len(content)} bytes)")
Knowledge & playbook management
Manage the context and instructions that Devin uses across sessions.
Create a knowledge note
curl -X POST "https://api.devin.ai/v3/organizations/$DEVIN_ORG_ID/knowledge/notes" \
-H "Authorization: Bearer $DEVIN_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Coding standards",
"trigger": "When writing code in any repository",
"body": "Use TypeScript strict mode. Follow existing code style. Run lint before committing."
}'
List knowledge notes
curl "https://api.devin.ai/v3/organizations/$DEVIN_ORG_ID/knowledge/notes" \
-H "Authorization: Bearer $DEVIN_API_KEY"
Update a knowledge note
curl -X PUT "https://api.devin.ai/v3/organizations/$DEVIN_ORG_ID/knowledge/notes/$NOTE_ID" \
-H "Authorization: Bearer $DEVIN_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Coding standards (updated)",
"trigger": "When writing code in any repository",
"body": "Use TypeScript strict mode. Follow existing code style. Run lint and type-check before committing."
}'
Delete a knowledge note
curl -X DELETE "https://api.devin.ai/v3/organizations/$DEVIN_ORG_ID/knowledge/notes/$NOTE_ID" \
-H "Authorization: Bearer $DEVIN_API_KEY"
Create a playbook
curl -X POST "https://api.devin.ai/v3/organizations/$DEVIN_ORG_ID/playbooks" \
-H "Authorization: Bearer $DEVIN_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "PR Review",
"instructions": "Review the PR for bugs, security issues, and style violations. Leave inline comments."
}'
Full Python example
import os
import requests
API_KEY = os.environ["DEVIN_API_KEY"]
ORG_ID = os.environ["DEVIN_ORG_ID"]
BASE = "https://api.devin.ai/v3"
HEADERS = {
"Authorization": f"Bearer {API_KEY}",
"Content-Type": "application/json"
}
# Create knowledge note
note = requests.post(
f"{BASE}/organizations/{ORG_ID}/knowledge/notes",
headers=HEADERS,
json={
"name": "Coding standards",
"trigger": "When writing code in any repository",
"body": "Use TypeScript strict mode. Follow existing code style."
}
).json()
print(f"Created note: {note['note_id']}")
# List all notes
notes = requests.get(
f"{BASE}/organizations/{ORG_ID}/knowledge/notes",
headers={"Authorization": f"Bearer {API_KEY}"}
).json()["items"]
print(f"Total notes: {len(notes)}")
# Create playbook
playbook = requests.post(
f"{BASE}/organizations/{ORG_ID}/playbooks",
headers=HEADERS,
json={
"name": "PR Review",
"instructions": "Review the PR for bugs, security issues, and style violations."
}
).json()
print(f"Created playbook: {playbook['playbook_id']}")
Scheduling automated sessions
Create recurring sessions that run on a schedule.
Create a schedule
curl -X POST "https://api.devin.ai/v3/organizations/$DEVIN_ORG_ID/schedules" \
-H "Authorization: Bearer $DEVIN_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"prompt": "Run the test suite and report any failures",
"cron_schedule": "0 9 * * 1-5",
"timezone": "America/New_York"
}'
This creates a schedule that runs every weekday at 9 AM Eastern.
List schedules
curl "https://api.devin.ai/v3/organizations/$DEVIN_ORG_ID/schedules" \
-H "Authorization: Bearer $DEVIN_API_KEY"
Update a schedule
curl -X PATCH "https://api.devin.ai/v3/organizations/$DEVIN_ORG_ID/schedules/$SCHEDULE_ID" \
-H "Authorization: Bearer $DEVIN_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"cron_schedule": "0 8 * * 1-5",
"is_enabled": true
}'
Delete a schedule
curl -X DELETE "https://api.devin.ai/v3/organizations/$DEVIN_ORG_ID/schedules/$SCHEDULE_ID" \
-H "Authorization: Bearer $DEVIN_API_KEY"
Full Python example
import os
import requests
API_KEY = os.environ["DEVIN_API_KEY"]
ORG_ID = os.environ["DEVIN_ORG_ID"]
BASE = "https://api.devin.ai/v3"
HEADERS = {
"Authorization": f"Bearer {API_KEY}",
"Content-Type": "application/json"
}
# Create a daily health check schedule
schedule = requests.post(
f"{BASE}/organizations/{ORG_ID}/schedules",
headers=HEADERS,
json={
"prompt": "Run the test suite and report any failures",
"cron_schedule": "0 9 * * 1-5",
"timezone": "America/New_York"
}
).json()
print(f"Created schedule: {schedule['schedule_id']}")
# List all schedules
schedules = requests.get(
f"{BASE}/organizations/{ORG_ID}/schedules",
headers={"Authorization": f"Bearer {API_KEY}"}
).json()["items"]
for s in schedules:
status = "enabled" if s.get("is_enabled") else "disabled"
print(f" {s['schedule_id']}: {s['cron_schedule']} ({status})")
Hand off a task from anywhere
Because the Sessions API creates a cloud Devin session from a single request, any tool, script, or coding agent can “hand off” work to Devin — bundling the current repo, branch, and uncommitted changes into the prompt so the cloud session picks up where you left off.
Create a session with repo context
# Build the JSON body with jq so the raw diff — which contains quotes,
# backslashes, and newlines — is escaped into a valid JSON string.
curl -X POST "https://api.devin.ai/v3/organizations/$DEVIN_ORG_ID/sessions" \
-H "Authorization: Bearer $DEVIN_API_KEY" \
-H "Content-Type: application/json" \
-d "$(jq -n --arg diff "$(git diff HEAD)" \
'{prompt: "Repo: my-org/my-repo (branch: fix-flaky-tests)\nFix the flaky integration tests in CI.\n\nUncommitted changes:\n\($diff)"}')"
The cloud session clones the repo, applies the context from your prompt, and runs in its own VM with a shell, browser, and full repo access. Track it by polling for messages or in the Devin web app.
git diff HEAD can include uncommitted secrets — API keys, tokens, or .env edits — and the prompt is uploaded to the cloud session. Review your diff and commit, stash, or remove sensitive changes before handing off.
Don’t want to build this yourself? The open-source Devin Handoff plugin wraps exactly this flow — auto-detecting repo, branch, and diff — so you can hand off from the Devin CLI, Claude Code, Codex, Cursor, or a plain shell script. See Hand off to Devin.
Error handling
All examples above should include error handling in production. Here’s a reusable pattern:
import requests
def api_request(method, url, headers, **kwargs):
"""Make an API request with standard error handling."""
response = requests.request(method, url, headers=headers, **kwargs)
try:
response.raise_for_status()
return response.json()
except requests.exceptions.HTTPError as e:
status = e.response.status_code
if status == 401:
raise Exception("Invalid or expired API key")
elif status == 403:
raise Exception("Service user lacks required permission")
elif status == 404:
raise Exception("Resource not found")
elif status == 429:
raise Exception("Rate limit exceeded — wait and retry")
else:
raise Exception(f"API error {status}: {e.response.text}")
Support