Tab
Inline completions for any editor, through an OpenAI-compatible endpoint.
Tab suggests what you'll write next and shows it as ghost text at the cursor. Press Tab to accept it, Escape to dismiss it, or keep typing to ignore it. It's built into Coline's editors, and the same completions are available to your own editors through the API.
In Coline
Tab works in Notes, Docs, the Messages composer, task descriptions and fields, calendar event descriptions, and the Code editor. Suggestions use the text around the cursor, the document's title, and your local date. Turn it off in your account settings if you don't want it.
API
POST https://coline.app/api/v1/tab/chat/completionsThe endpoint follows the OpenAI chat completions format, so OpenAI-compatible clients work with it. Coline-specific context goes in tab_context. It needs an API key or OAuth token with the ai.invoke scope, and is billed to the workspace's API credits.
Models
GET /api/v1/tab/models lists models and prices. It needs no auth.
| Model | Surfaces | Input, per million tokens | Output, per million tokens |
|---|---|---|---|
tab-v1 (default) | any, notes, docs, messages, tasks, calendar | $0.20 | $0.50 |
tab-1-code | code | $0.08 | $0.28 |
Request
curl https://coline.app/api/v1/tab/chat/completions \
-H "Authorization: Bearer col_ws_..." \
-H "Content-Type: application/json" \
-d '{
"model": "tab-v1",
"stream": true,
"max_completion_tokens": 100,
"tab_context": {
"surface": "docs",
"entity_id": "doc_123",
"document_type": "markdown",
"document_title": "Q3 planning",
"active_text_before_cursor": "Our goals for the quarter are ",
"active_text_after_cursor": ""
}
}'| Field | ||
|---|---|---|
model | Optional | tab-v1 by default. Use tab-1-code with the code surface |
stream | Optional | true streams Server-Sent Events. Recommended |
max_completion_tokens | Optional | 1 to 500. Defaults to 500. max_tokens also works |
temperature, top_p | Optional | Temperature defaults to 0.2 |
messages | Optional | If you don't send tab_context, the last user message is used as the text before the cursor |
tab_context | Recommended | See below |
tab_context:
| Field | ||
|---|---|---|
surface | Required | notes, docs, messages, tasks, calendar, code, or any short name for your editor (any works too) |
active_text_before_cursor | Required | Up to 16,000 characters |
active_text_after_cursor | Optional | Up to 16,000 characters |
surrounding_context | Optional | Other useful context, up to 32,000 characters |
document_type | Optional | markdown (default) or plaintext |
entity_id | Optional | An ID for the document or file, for caching and analytics |
workspace_slug | Optional | Defaults to the API key's workspace |
document_title, note_title | Optional | Title of the document being edited |
block_kind | Optional | The kind of block at the cursor, such as paragraph or bulleted-list |
code_language, file_path | Code | For the code surface |
completion_mode | Code | line for one line, block for several |
client_local_date | Optional | The person's local date, such as 2026-10-12 Mon, so relative dates come out right |
taskboard_file_id | Tasks | Required when surface is tasks |
Response
Streaming responses are standard chat completion chunks, ending with [DONE]:
data: {"object":"chat.completion.chunk","choices":[{"index":0,"delta":{"content":" shipping"},"finish_reason":null}]}
data: {"object":"chat.completion.chunk","choices":[{"index":0,"delta":{"content":" the new"},"finish_reason":null}]}
data: [DONE]Without streaming you get one chat.completion object with the text in choices[0].message.content and token counts in usage.
Insert the text at the cursor as ghost text. Tab doesn't repeat what's already after the cursor.
Credits
Each call reserves the most it could cost from the workspace's API credit balance, then refunds the difference when it finishes. If the balance is too low, the call fails with 402 INSUFFICIENT_CREDITS. Add credits in the developer console under Credits; the minimum purchase is $5. The console's Tab playground lets you try completions first.