Aamu exposes a project API for teams and AI agents that need to create, read, and update real work inside Aamu. The API follows the same building blocks people use in the UI: tasks, docs, meetings, forms, files, databases, and database rows through GraphQL.
The API is described by an OpenAPI document at /.well-known/openapi.json. That document is useful both for humans and for AI tools that need to discover available operations.
Authentication and project scope
Every API request uses a Team API key in the x-api-key header. Project-scoped resources also use x-project-id. When an API key has access to multiple projects, the project header disambiguates which project the request should use.
x-api-key: YOUR_API_KEY
x-project-id: YOUR_PROJECT_IDAPI keys can be scoped by feature and permission. For example, one key can have read-only Docs access, while another can create tasks, upload files, and submit forms in selected projects.
API actor
Write operations can set the acting user with x-aamu-actor. The value can be a username, such as ai or badding, or a user id. The actor must be a member of the scoped project.
When the header is omitted, Aamu uses the project ai user when available, otherwise the project owner. The same fallback applies across project item writes: tasks, docs, meetings, form submissions, file upload registration, and database creation/schema operations.
x-aamu-actor: aiAPI key introspection
Integrations can inspect the API key they are currently using with GET /api/v1/key. This is useful for setup checks, debugging missing permissions, and choosing the right project header when a key has multiple scopes.
The endpoint returns safe metadata only. It never returns the API key value, salt, hash, or any other secret used to validate the key.
GET: inspect current API key
GET /api/v1/key
x-api-key: YOUR_API_KEYExample response:
{
"key": {
"id": "API_KEY_ID",
"name": "Support automation",
"permissions": ["read", "write", "comment"],
"scopes": [
{
"feature": "helpdesk",
"resource_id": "PROJECT_ID",
"permissions": ["read", "write", "comment"]
},
{
"feature": "team-brain",
"resource_id": "PROJECT_ID",
"permissions": ["read", "write", "comment"]
}
],
"created": 1780000000000,
"created_by": "USER_ID",
"last_used_at": 1780000000000,
"expires_at": null
}
}Use the returned scopes list to confirm which features the key can access. Project-scoped endpoints still use x-project-id when the project needs to be disambiguated.
Team Brain
Team Brain is the shared knowledge layer behind Aamu AI. It can be queried directly through the API when an integration needs grounded context before it writes a task, drafts a support reply, creates a doc, or decides what to do next.
The retrieve endpoint returns matching curated Team Brain entries and source chunks. It does not generate a final answer by itself; callers can use the results as context for their own model, or let Aamu use the same knowledge through a workflow such as Helpdesk reply-draft generation.
POST: retrieve knowledge
limit controls the maximum number of matching results returned. Results are ranked by relevance score, so a smaller limit is useful when an AI prompt needs only the strongest few pieces of context, while a larger limit gives the caller more material to inspect or rerank.
POST /api/v1/team-brain/retrieve
x-api-key: YOUR_API_KEY
x-project-id: YOUR_PROJECT_ID
Content-Type: application/json
{
"query": "How should we answer a billing cancellation question?",
"limit": 8
}Example response:
{
"results": [
{
"kind": "brain",
"score": 0.82,
"title": "Cancellation billing policy",
"text": "Explain the policy clearly and ask for the account email if needed.",
"urls": []
}
]
}Use Team Brain read scope for the project. When Helpdesk draft generation is configured to use Team Brain, the same Team Brain read scope is required in addition to Helpdesk write scope.
Users
The Users API resolves project members for actor headers, task assignees and integrations that need stable Aamu user ids. It accepts any project read scope, such as Tasks, Docs, Helpdesk or Emails.
GET: list users
GET /api/v1/users/
x-api-key: YOUR_API_KEY
x-project-id: YOUR_PROJECT_IDThe older ?username=... query filter is still supported for backwards compatibility, but one-user lookups should use the resource endpoint below.
GET: get one user
GET /api/v1/users/badding
x-api-key: YOUR_API_KEY
x-project-id: YOUR_PROJECT_IDThe path value can be a username or user id. Example response:
{
"user": {
"id": "USER_ID",
"username": "badding",
"name": "Badding",
"email": "badding@example.com"
}
}Helpdesk
The Helpdesk API is designed for human-in-the-loop support automation. Integrations can read new tickets, prepare reply drafts, and send a draft only through an explicit send command.
Reply drafts are stored in the same user-specific comment draft location the UI uses, and the ticket is marked with hasDraft. In practice this means the draft appears in the Helpdesk reply editor for the API actor, ready for a human to review and send.
GET: list tickets
GET /api/v1/helpdesk/tickets/?status=open&unanswered=true
x-api-key: YOUR_API_KEY
x-project-id: YOUR_PROJECT_IDGET: resolve a Helpdesk actor
Use this endpoint to check a Helpdesk actor and the project Helpdesk mailbox used for email tickets. The actor can be a username or user id. The response includes safe mailbox metadata only, never passwords or tokens.
GET /api/v1/helpdesk/actors/ile
x-api-key: YOUR_API_KEY
x-project-id: YOUR_PROJECT_IDExample response:
{
"actor": {
"id": "USER_ID",
"username": "ile",
"name": "Ilkka Huotari",
"email": "user-account@example.com",
"helpdesk_name": "Ilkka",
"mailbox": {
"email": "support@example.com",
"name": "Support",
"configured": true
}
}
}PUT: write a reply draft
PUT /api/v1/helpdesk/tickets/TICKET_ID/reply-draft
x-api-key: YOUR_API_KEY
x-project-id: YOUR_PROJECT_ID
x-aamu-actor: ai
Content-Type: application/json
{
"html": "<p>Hei, kiitos viestistä. Tarkistin tilanteen ja...</p>",
"mode": "replace"
}POST: generate a reply draft
Aamu can also generate the draft with Team AI. By default this uses Team Brain retrieval as context, so the API key needs both Helpdesk write scope and Team Brain read scope for the project.
POST /api/v1/helpdesk/tickets/TICKET_ID/reply-draft/generate
x-api-key: YOUR_API_KEY
x-project-id: YOUR_PROJECT_ID
x-aamu-actor: ai
Content-Type: application/json
{
"instructions": "Answer in Finnish, friendly and concise.",
"use_team_brain": true,
"mode": "replace"
}The generated draft is saved as the API actor’s Helpdesk comment draft. It is not sent automatically.
POST: send a reply draft
Sending is deliberately separate from writing or generating a draft. The send endpoint takes the current draft for the API actor, sends it through the same Helpdesk comment/email path as the UI, clears the draft, and returns the sent comment plus the updated ticket.
Use Helpdesk comment permission for this endpoint. x-aamu-actor selects whose draft is sent, so integrations can safely keep AI-generated drafts under an ai actor or send as a specific user when that is intended.
POST /api/v1/helpdesk/tickets/TICKET_ID/reply-draft/send
x-api-key: YOUR_API_KEY
x-project-id: YOUR_PROJECT_ID
x-aamu-actor: aiExample response:
{
"comment_path": "comments.3",
"comment": {
"id": "COMMENT_ID",
"html": "<p>Hei, kiitos viestistä...</p>",
"from": "ai"
},
"ticket": {
"id": "TICKET_ID",
"has_draft": false
}
}Emails
The Email API follows the same human-in-the-loop model as Helpdesk: integrations can list email threads, write or generate a user-specific reply draft, and send that draft only through an explicit send command.
Drafts are stored in the same user-specific comment draft location the email UI uses and the email is marked with hasDraft. Use x-aamu-actor to select whose draft is written, generated or sent.
GET: list emails
limit controls the maximum number of email threads returned. Use status=unanswered or unanswered=true when building an AI reply queue.
GET /api/v1/emails/?status=unanswered&limit=20
x-api-key: YOUR_API_KEY
x-project-id: YOUR_PROJECT_IDGET: resolve an email actor
Use this endpoint to check which project-specific email address an actor uses when sending email replies. The actor can be a username or user id. The response includes safe mailbox metadata only, never passwords or tokens.
GET /api/v1/emails/actors/ile
x-api-key: YOUR_API_KEY
x-project-id: YOUR_PROJECT_IDExample response:
{
"actor": {
"id": "USER_ID",
"username": "ile",
"name": "Ilkka Huotari",
"email": "user-account@example.com",
"mailbox": {
"email": "project-mailbox@example.com",
"name": "Kansalaiskeskustelu",
"configured": true
}
}
}GET: search email contacts
Use contacts when selecting recipients for new drafts or sent emails. The API also auto-resolves address-only recipients to existing contacts by email address. Missing contacts are created only when create_missing_contacts is explicitly set to true.
GET /api/v1/emails/contacts/?q=ilkkah&limit=10
x-api-key: YOUR_API_KEY
x-project-id: YOUR_PROJECT_ID
x-aamu-actor: ilePOST: create a new email draft
Use this endpoint to create a new outbound email draft for the API actor without sending it. The draft appears in the same email draft list the UI uses. Use Emails write permission for this endpoint.
POST /api/v1/emails/drafts/
x-api-key: YOUR_API_KEY
x-project-id: YOUR_PROJECT_ID
x-aamu-actor: ile
Content-Type: application/json
{
"subject": "Draft from Aamu API",
"html": "<p>This draft has not been sent.</p>",
"to": [
{ "address": "ilkkah@gmail.com", "name": "Ilkka" }
],
"create_missing_contacts": true
}POST: send a new email
Use this endpoint to send a new outbound email, not a reply to an existing thread. It creates a draft for the API actor, sends it through the same project email path as the UI, and returns the sent email thread. Use Emails comment permission for this endpoint.
POST /api/v1/emails/
x-api-key: YOUR_API_KEY
x-project-id: YOUR_PROJECT_ID
x-aamu-actor: ile
Content-Type: application/json
{
"subject": "Test email from Aamu API",
"html": "<p>Hello from the Aamu Email API.</p>",
"to": [
{ "address": "ilkkah@gmail.com", "name": "Ilkka" }
]
}PUT: write a reply draft
The API chooses the original sender/contact as the default recipient when possible. You can pass to explicitly if the integration wants to override recipients.
PUT /api/v1/emails/EMAIL_ID/reply-draft
x-api-key: YOUR_API_KEY
x-project-id: YOUR_PROJECT_ID
x-aamu-actor: ai
Content-Type: application/json
{
"html": "<p>Hei, kiitos viestistä. Palaamme tähän pian.</p>",
"mode": "replace",
"to": [
{ "address": "customer@example.com", "name": "Customer" }
]
}POST: generate a reply draft
Email draft generation supports optional instructions for tone, language or constraints. By default it uses Team Brain retrieval as context, so the API key needs both Emails write scope and Team Brain read scope for the project.
POST /api/v1/emails/EMAIL_ID/reply-draft/generate
x-api-key: YOUR_API_KEY
x-project-id: YOUR_PROJECT_ID
x-aamu-actor: ai
Content-Type: application/json
{
"instructions": "Answer in Finnish and keep it short.",
"use_team_brain": true,
"mode": "replace"
}POST: send a reply draft
Sending uses the same email-comment/send path as the UI. The endpoint sends the current draft for the API actor, clears it, and returns the sent comment plus the updated email thread. Use Emails comment permission for this endpoint.
POST /api/v1/emails/EMAIL_ID/reply-draft/send
x-api-key: YOUR_API_KEY
x-project-id: YOUR_PROJECT_ID
x-aamu-actor: aiTasks
The Tasks API is useful for turning external events, AI plans, and support workflows into actionable work. Task create and update operations use the same internal task helpers as the UI for fields that have side effects, such as status, assigned users, dates, repetition, and reminders.
GET: list tasks
GET /api/v1/tasks/
x-api-key: YOUR_API_KEY
x-project-id: YOUR_PROJECT_IDExample response:
{
"tasks": [
{
"id": "TASK_ID",
"pid": "YOUR_PROJECT_ID",
"title": "Review API integration",
"html": "<p>Check the rollout notes.</p>",
"status": "active",
"start_at": 1779872400000,
"end_at": 1779876000000,
"repeat": "weekly",
"reminders": [
{ "d": 0, "h": 0, "m": 15 }
],
"users": ["USER_ID"],
"comments": []
}
]
}GET: list project users
Use the Users endpoint to resolve usernames to user ids before assigning task users or choosing an API actor.
GET /api/v1/users/
x-api-key: YOUR_API_KEY
x-project-id: YOUR_PROJECT_IDYou can also filter by exact username:
GET /api/v1/users/?username=badding
x-api-key: YOUR_API_KEY
x-project-id: YOUR_PROJECT_IDExample response:
{
"users": [
{
"id": "USER_ID",
"username": "badding",
"name": "badding",
"email": "person@example.com"
}
]
}POST: create a task
Create accepts the same task workflow fields as update. If users is provided, the API applies the assignment changes through the same task user helpers as the UI. Dates, repetition, and reminders go through the task date-change helper.
POST /api/v1/tasks/
x-api-key: YOUR_API_KEY
x-project-id: YOUR_PROJECT_ID
x-aamu-actor: ai
Content-Type: application/json
{
"title": "Prepare customer summary",
"html": "<p>Summarize the latest feedback and add next steps.</p>",
"status": "active",
"users": ["USER_ID_1", "USER_ID_2"],
"start_at": "2026-05-27T09:00:00.000Z",
"end_at": "2026-05-27T10:00:00.000Z",
"reminders": [
{ "minutes_before": 15 },
{ "d": 0, "h": 1, "m": 0 }
],
"comments": [
{ "html": "<p>Created from the external workflow.</p>" }
]
}For repeating tasks, set repeat or repetition to daily, weekly, monthly, or yearly. Leave it out for a one-time task.
Example response:
{
"task": {
"id": "TASK_ID",
"pid": "YOUR_PROJECT_ID",
"title": "Prepare customer summary",
"html": "<p>Summarize the latest feedback and add next steps.</p>",
"status": "active",
"start_at": 1779872400000,
"end_at": 1779876000000,
"reminders": [
{ "d": 0, "h": 0, "m": 15 },
{ "d": 0, "h": 1, "m": 0 }
],
"users": ["USER_ID_1", "USER_ID_2"],
"comments": []
}
}PATCH: update a task
Update supports title, HTML, status, assigned users, start and end dates, repetition, and reminders. Fields with side effects are routed through the same internal helpers as UI operations.
PATCH /api/v1/tasks/TASK_ID
x-api-key: YOUR_API_KEY
x-project-id: YOUR_PROJECT_ID
x-aamu-actor: ai
Content-Type: application/json
{
"title": "Prepare customer summary and next steps",
"html": "<p>Summarize feedback, risks, and next actions.</p>",
"status": "complete",
"users": ["USER_ID_1", "USER_ID_2"],
"start_at": 1779872400000,
"end_at": 1779876000000,
"repeat": "weekly",
"reminders": [
{ "minutes_before": 15 }
]
}Use null to clear start_at, end_at, repeat, or reminders. Use an empty reminder array to remove all reminders.
Example response:
{
"task": {
"id": "TASK_ID",
"title": "Prepare customer summary and next steps",
"html": "<p>Summarize feedback, risks, and next actions.</p>",
"status": "complete",
"start_at": 1779872400000,
"end_at": 1779876000000,
"repeat": "weekly",
"reminders": [
{ "d": 0, "h": 0, "m": 15 }
],
"users": ["USER_ID_1", "USER_ID_2"]
}
}Task comments
Task comments use the same HTML-oriented content model as tasks. Comments are returned as part of the task response.
GET: read a task with comments
GET /api/v1/tasks/TASK_ID
x-api-key: YOUR_API_KEY
x-project-id: YOUR_PROJECT_IDExample response:
{
"task": {
"id": "TASK_ID",
"title": "Prepare customer summary",
"html": "<p>Summarize the latest feedback.</p>",
"comments": [
{
"id": "COMMENT_ID",
"html": "<p>Draft summary added.</p>"
}
]
}
}POST: add a comment
POST /api/v1/tasks/TASK_ID/comments
x-api-key: YOUR_API_KEY
x-project-id: YOUR_PROJECT_ID
Content-Type: application/json
{
"html": "<p>I reviewed the customer notes and added follow-up actions.</p>"
}Example response:
{
"task": {
"id": "TASK_ID",
"comments": [
{
"id": "COMMENT_ID",
"html": "<p>I reviewed the customer notes and added follow-up actions.</p>"
}
]
}
}Docs
The Docs API creates durable written material directly into Aamu. It is a good fit for AI-generated summaries, runbooks, meeting notes, release notes, customer handoff documents, and internal knowledge articles.
GET: list docs
GET /api/v1/docs/
x-api-key: YOUR_API_KEY
x-project-id: YOUR_PROJECT_IDExample response:
{
"docs": [
{
"id": "DOC_ID",
"pid": "YOUR_PROJECT_ID",
"title": "Weekly API Report",
"status": "public",
"html": ""
}
]
}POST: create a doc
POST /api/v1/docs/
x-api-key: YOUR_API_KEY
x-project-id: YOUR_PROJECT_ID
Content-Type: application/json
{
"title": "Weekly API Report",
"html": "<h1>Weekly API Report</h1><p>All checks passed.</p>"
}Example response:
{
"doc": {
"id": "DOC_ID",
"pid": "YOUR_PROJECT_ID",
"title": "Weekly API Report",
"status": "public",
"html": "<h1>Weekly API Report</h1><p>All checks passed.</p>"
}
}PATCH: update a doc
PATCH /api/v1/docs/DOC_ID
x-api-key: YOUR_API_KEY
x-project-id: YOUR_PROJECT_ID
Content-Type: application/json
{
"title": "Weekly API Report, revised",
"html": "<h1>Weekly API Report</h1><p>All checks passed after the retry.</p>"
}Example response:
{
"doc": {
"id": "DOC_ID",
"title": "Weekly API Report, revised",
"html": "<h1>Weekly API Report</h1><p>All checks passed after the retry.</p>"
}
}Meetings
The Meetings API can create and update project meetings. It supports fields such as name, HTML description, start time, end time, and invitee emails.
GET: list meetings
GET /api/v1/meetings/
x-api-key: YOUR_API_KEY
x-project-id: YOUR_PROJECT_IDExample response:
{
"meetings": [
{
"id": "MEETING_ID",
"pid": "YOUR_PROJECT_ID",
"name": "API rollout review",
"status": "public",
"html": "<p>Review integration status.</p>",
"start_time": 1779458400000,
"end_time": 1779462000000
}
]
}POST: create a meeting
POST /api/v1/meetings/
x-api-key: YOUR_API_KEY
x-project-id: YOUR_PROJECT_ID
Content-Type: application/json
{
"name": "API rollout review",
"html": "<p>Review integration status and next steps.</p>",
"start_time": 1779458400000,
"end_time": 1779462000000
}Example response:
{
"meeting": {
"id": "MEETING_ID",
"pid": "YOUR_PROJECT_ID",
"name": "API rollout review",
"status": "public",
"html": "<p>Review integration status and next steps.</p>"
}
}PATCH: update a meeting
PATCH /api/v1/meetings/MEETING_ID
x-api-key: YOUR_API_KEY
x-project-id: YOUR_PROJECT_ID
Content-Type: application/json
{
"name": "API rollout review, updated",
"html": "<p>Review production results and decide follow-up actions.</p>",
"start_time": 1779462000000,
"end_time": 1779465600000
}Example response:
{
"meeting": {
"id": "MEETING_ID",
"name": "API rollout review, updated",
"html": "<p>Review production results and decide follow-up actions.</p>",
"start_time": 1779462000000,
"end_time": 1779465600000
}
}Forms API
The authenticated Forms API lets integrations list forms, inspect fields, and submit responses. A submitted response creates a row in the database table connected to the form.
GET: list forms
GET /api/v1/forms/
x-api-key: YOUR_API_KEY
x-project-id: YOUR_PROJECT_IDExample response:
{
"forms": [
{
"id": "FORM_ID",
"pid": "YOUR_PROJECT_ID",
"name": "Feedback form",
"status": "public",
"table_id": "TABLE_ID",
"fields": [
{ "id": "COLUMN_ID", "name": "Email", "type": "text", "gtype": "email" },
{ "id": "COLUMN_ID_2", "name": "Message", "type": "longtext", "gtype": "message" }
]
}
]
}POST: submit a form response
POST /api/v1/forms/FORM_ID/submissions
x-api-key: YOUR_API_KEY
x-project-id: YOUR_PROJECT_ID
Content-Type: application/json
{
"fields": {
"email": "person@example.com",
"message": "I would like to hear more."
}
}Example response:
{
"submission": {
"id": "ROW_ID",
"form_id": "FORM_ID",
"db_id": "DB_ID",
"table_id": "TABLE_ID"
}
}Public forms
Public browser forms use the same URL for viewing and submitting. They do not use a Team API key. This is separate from the authenticated Forms API.
GET: render a public form
GET /shared/form/FORM_IDExample response is HTML:
<form action="https://your-team.aamu.app/shared/form/FORM_ID" method="post" enctype="multipart/form-data">
<input type="hidden" name="form_builder" value="1">
...
</form>POST: submit a public form
POST /shared/form/FORM_ID
Content-Type: multipart/form-data
form_builder=1
email=person@example.com
message=I would like to hear more.Example response is usually a redirect to the form thank-you page. For AJAX-style multipart submissions, the response can be JSON:
{
"success": true,
"id": "ROW_ID"
}Files
Aamu’s editor uses relative file URLs such as /file/browser/{filepointer_id}/{file_version_id}/{name}. The Files API follows the same pattern, so API-created content works with the UI editor.
GET: read file metadata
GET /api/v1/files/FILEPOINTER_ID
x-api-key: YOUR_API_KEY
x-project-id: YOUR_PROJECT_IDExample response:
{
"file": {
"id": "FILEPOINTER_ID",
"filepointer_id": "FILEPOINTER_ID",
"file_id": "FILE_VERSION_ID",
"file_version_id": "FILE_VERSION_ID",
"name": "example.png",
"type": "image/png",
"size": 12345,
"browser_url": "/file/browser/FILEPOINTER_ID/FILE_VERSION_ID/example.png",
"download_url": "/file/dl/FILEPOINTER_ID/FILE_VERSION_ID/example.png"
}
}POST: upload a file
Uploading uses two API POSTs and one signed PUT. First prepare the upload:
POST /api/v1/files/prepare-upload
x-api-key: YOUR_API_KEY
x-project-id: YOUR_PROJECT_ID
Content-Type: application/json
{
"name": "example.png",
"type": "image/png",
"size": 12345
}Example response:
{
"upload": {
"method": "PUT",
"url": "SIGNED_UPLOAD_URL",
"headers": { "Content-Type": "image/png" }
},
"complete": {
"file_id": "FILE_VERSION_ID",
"bucket": "files",
"key": "UPLOAD_KEY",
"name": "example.png",
"type": "image/png",
"size": 12345
}
}After uploading the bytes to SIGNED_UPLOAD_URL, complete the upload:
POST /api/v1/files/complete-upload
x-api-key: YOUR_API_KEY
x-project-id: YOUR_PROJECT_ID
Content-Type: application/json
{
"file_id": "FILE_VERSION_ID",
"bucket": "files",
"key": "UPLOAD_KEY",
"name": "example.png",
"type": "image/png",
"size": 12345
}Example response:
{
"file": {
"id": "FILEPOINTER_ID",
"file_id": "FILE_VERSION_ID",
"browser_url": "/file/browser/FILEPOINTER_ID/FILE_VERSION_ID/example.png",
"download_url": "/file/dl/FILEPOINTER_ID/FILE_VERSION_ID/example.png"
}
}The browser_url can be embedded in Docs or Tasks HTML. The old item-level files field is deprecated and is not returned by the current API.
Databases and GraphQL
The REST Database API creates databases and changes table schema. Row data is handled through the generated GraphQL API. In other words, schema setup uses REST, while row reads and writes use GraphQL.
GET: read row data with GraphQL
GraphQL reads are sent as HTTP POST requests to /api/v1/graphql/. This is normal GraphQL behavior: the operation is a read, even though the HTTP method is POST.
POST /api/v1/graphql/
x-api-key: YOUR_API_KEY
x-db-id: DB_ID
Content-Type: application/json
{
"query": "query { feedbackRows { id customer message status sourceDocument } }"
}Example response:
{
"data": {
"feedbackRows": [
{
"id": "ROW_ID",
"customer": "Ada Lovelace",
"message": "The onboarding flow was clear.",
"status": "new",
"sourceDocument": "DOC_ID"
}
]
}
}POST: create a database
POST /api/v1/databases/
x-api-key: YOUR_API_KEY
x-project-id: YOUR_PROJECT_ID
Content-Type: application/json
{
"name": "Customer feedback"
}Example response:
{
"database": {
"id": "DB_ID",
"pid": "YOUR_PROJECT_ID",
"name": "Customer feedback",
"tables": ["TABLE_ID"],
"table_id": "TABLE_ID"
}
}POST: add columns
POST /api/v1/databases/DB_ID/tables/TABLE_ID/columns
x-api-key: YOUR_API_KEY
Content-Type: application/json
{
"columns": [
{ "name": "Customer", "type": "text", "gtype": "customer" },
{ "name": "Message", "type": "longtext", "gtype": "message" },
{ "name": "Status", "type": "status", "gtype": "status" },
{ "name": "Source document", "type": "document", "gtype": "sourceDocument" }
]
}Example response:
{
"columns": [
{ "id": "COLUMN_ID", "name": "Customer", "type": "text", "gtype": "customer" },
{ "id": "COLUMN_ID_2", "name": "Message", "type": "longtext", "gtype": "message" },
{ "id": "COLUMN_ID_3", "name": "Status", "type": "status", "gtype": "status" },
{ "id": "COLUMN_ID_4", "name": "Source document", "type": "document", "gtype": "sourceDocument" }
]
}POST: add row data with GraphQL
POST /api/v1/graphql/
x-api-key: YOUR_API_KEY
x-db-id: DB_ID
Content-Type: application/json
{
"query": "mutation CreateFeedback($input: FeedbackInput!) { createFeedback(input: $input) { id customer message status sourceDocument } }",
"variables": {
"input": {
"customer": "Ada Lovelace",
"message": "The onboarding flow was clear.",
"status": "new",
"sourceDocument": "DOC_ID"
}
}
}Example response:
{
"data": {
"createFeedback": {
"id": "ROW_ID",
"customer": "Ada Lovelace",
"message": "The onboarding flow was clear.",
"status": "new",
"sourceDocument": "DOC_ID"
}
}
}POST: update row data with GraphQL
GraphQL updates are also sent to /api/v1/graphql/ with HTTP POST. The mutation name depends on the generated schema, so use introspection to confirm the exact name and input type.
POST /api/v1/graphql/
x-api-key: YOUR_API_KEY
x-db-id: DB_ID
Content-Type: application/json
{
"query": "mutation UpdateFeedback($id: ID!, $input: FeedbackInput!) { updateFeedback(id: $id, input: $input) { id customer message status sourceDocument } }",
"variables": {
"id": "ROW_ID",
"input": {
"customer": "Ada Lovelace",
"message": "The onboarding flow was clear, and examples would help advanced users.",
"status": "reviewed",
"sourceDocument": "DOC_ID"
}
}
}Example response:
{
"data": {
"updateFeedback": {
"id": "ROW_ID",
"customer": "Ada Lovelace",
"message": "The onboarding flow was clear, and examples would help advanced users.",
"status": "reviewed",
"sourceDocument": "DOC_ID"
}
}
}Using Databases and Docs together
Docs and Databases work well together. A database can hold structured state, while Docs can hold rich long-form context. The bridge between the two is the document column type: its value is the id of a Docs document.
GET: fetch a linked Doc after querying a row
GET /api/v1/docs/DOC_ID
x-api-key: YOUR_API_KEY
x-project-id: YOUR_PROJECT_IDExample response:
{
"doc": {
"id": "DOC_ID",
"title": "Interview notes: Ada Lovelace",
"html": "<h1>Interview notes</h1><p>Ada liked the onboarding flow.</p>"
}
}POST: create a Doc and store its id in a row
First create the document:
POST /api/v1/docs/
x-api-key: YOUR_API_KEY
x-project-id: YOUR_PROJECT_ID
Content-Type: application/json
{
"title": "Interview notes: Ada Lovelace",
"html": "<h1>Interview notes</h1><p>Ada liked the onboarding flow and asked for more examples.</p>"
}Example response:
{
"doc": {
"id": "DOC_ID",
"title": "Interview notes: Ada Lovelace"
}
}Then use that DOC_ID in a database row whose column type is document:
POST /api/v1/graphql/
x-api-key: YOUR_API_KEY
x-db-id: DB_ID
Content-Type: application/json
{
"query": "mutation CreateFeedback($input: FeedbackInput!) { createFeedback(input: $input) { id customer sourceDocument } }",
"variables": {
"input": {
"customer": "Ada Lovelace",
"sourceDocument": "DOC_ID"
}
}
}Example response:
{
"data": {
"createFeedback": {
"id": "ROW_ID",
"customer": "Ada Lovelace",
"sourceDocument": "DOC_ID"
}
}
}Legacy direct table submit
Older generated database form snippets can submit directly to a table endpoint. This creates database rows, but it is best treated as a legacy/browser form integration.
GET: no row listing endpoint here
There is no recommended GET endpoint for this legacy table-submit surface. To read rows, use GraphQL instead.
POST /api/v1/graphql/
x-api-key: YOUR_API_KEY
x-db-id: DB_ID
Content-Type: application/json
{
"query": "query { feedbackRows { id customer message } }"
}Example response:
{
"data": {
"feedbackRows": [
{ "id": "ROW_ID", "customer": "Ada Lovelace", "message": "The onboarding flow was clear." }
]
}
}POST: legacy table submit
POST /api/v1/db/TABLE_ID
Content-Type: application/x-www-form-urlencoded
customer=Ada%20Lovelace&message=The%20onboarding%20flow%20was%20clear.Example response:
{
"success": true,
"id": "ROW_ID"
}New authenticated integrations should prefer /api/v1/forms/{id}/submissions for form submissions and /api/v1/graphql/ for database row operations.
Update support summary
Update support is intentionally feature-specific. Tasks, Docs, and Meetings expose REST PATCH endpoints. Database rows are updated through GraphQL mutations. Forms submissions and public form posts create rows; they are not update endpoints. Files are currently uploaded and fetched through the Files API, while replacing or deleting files should be modeled explicitly by the feature that references them.
Why this works well for AI agents
The Aamu API is intentionally close to the product model. An AI agent can create a task, attach files, write a doc, schedule a meeting, submit a form response, or work with structured database rows without inventing a parallel workflow.
The OpenAPI document gives the agent a map of the available operations, while scoped Team API keys keep access narrow. That combination makes the API useful for automation without making it too broad by default.
A practical starting point
For most integrations, the best first step is to generate a Team API key with the smallest useful set of project and feature scopes. Then call the OpenAPI document, inspect the schemas, and start with one workflow: create a task, write a doc, or upload a file and reference it from editor HTML.
From there, the same API surface can grow into richer workflows that use tasks for action, docs for knowledge, forms for input, files for context, meetings for coordination, and GraphQL databases for structured state.
