Tab API
OpenAI-compatible autocomplete for any editor.
Tab
Tab is Coline's intelligent autocomplete. Like Cursor Tab for code, but as a simple OpenAI-compatible primitive for any editor — code editors, notes apps, docs, email composers, issue trackers, messages, tasks, and more.
Tab predicts what you'll write next and shows it as ghost text. Press Tab to accept, or keep typing to ignore.
How It Works
- You type — Tab watches what you're writing
- Tab predicts — AI suggests the next words/lines
- Ghost text appears — The suggestion shows in muted text
- Press Tab to accept — Or just keep typing to ignore
Features
- Lightning fast — 120ms debounce, streaming completions
- Any surface — Works in code editors, notes apps, docs, email composers, issue trackers, messages, tasks, calendar, and custom editors
- Context aware — Understands your workspace, files, and conversations
- Natural flow — Matches your tone and writing style
- Multi-line — Suggests paragraphs, not just words
- Smart deduplication — Won't repeat what you already typed
Using Tab
Tab is built into the Coline UI. No API calls needed for normal usage — just start typing in any supported editor.
Keyboard Shortcuts
| Key | Action |
|---|---|
Tab | Accept the current suggestion |
Escape | Dismiss the suggestion |
| Continue typing | Ignore and keep writing |
Supported Surfaces
| Surface | Where It Works |
|---|---|
| Custom | Any editor that can send text before/after the cursor |
| Code | Public OpenAI-compatible editor integrations |
| Notes | Block-based editor, any text block |
| Docs | Rich text paragraphs, headings, lists |
| Messages | Channel and DM message composer |
| Tasks | Task descriptions and comments |
| Calendar | Event descriptions |
Tab API (Programmatic Access)
Build custom Tab experiences using the API.
Authentication
Same authentication as the rest of the Coline API:
Authorization: Bearer {api_key}Endpoints
List Models
GET /api/v1/tab/modelsReturns available Tab models.
Response:
{
"data": {
"models": [
{
"id": "tab-v1",
"object": "model",
"created": 1775200000,
"owned_by": "coline",
"pricing": {
"inputPerMillionUsd": 0.2,
"outputPerMillionUsd": 0.5
},
"capabilities": {
"streaming": true,
"surfaces": ["any", "code", "notes", "docs", "messages", "tasks", "calendar"]
}
}
]
}
}Create Completion
POST /api/v1/tab/chat/completionsGet an inline completion for the current cursor position.
Request Body:
{
model?: string; // "tab-v1" (default)
stream?: boolean; // true for streaming (recommended)
tab_context: {
surface: string; // "code", "notes", "email", "issue", etc.
workspace_slug: string; // Your workspace slug
entity_id: string; // File path, file ID, channel ID, etc.
document_type: "plaintext" | "markdown";
active_text_before_cursor: string; // Text before cursor (max 16k chars)
active_text_after_cursor?: string; // Text after cursor (max 16k chars)
surrounding_context?: string; // Additional context (max 32k chars)
code_language?: string; // For code: typescript, python, rust, etc.
file_path?: string; // For code: src/app.ts, lib/foo.py, etc.
block_kind?: string; // Optional editor-specific block/type hint
note_title?: string; // Note title for context
document_title?: string; // Doc title for context
taskboard_file_id?: string; // Required for tasks surface
};
max_completion_tokens?: number; // Max 500
}Example Request:
curl https://api.coline.app/v1/tab/chat/completions \
-H "Authorization: Bearer col_ws_xxx" \
-H "Content-Type: application/json" \
-d '{
"stream": true,
"tab_context": {
"surface": "docs",
"workspace_slug": "acme",
"entity_id": "doc_123",
"document_type": "markdown",
"active_text_before_cursor": "Our Q3 goals include ",
"document_title": "Q3 Planning"
},
"max_completion_tokens": 100
}'Code Editor Request:
curl https://api.coline.app/v1/tab/chat/completions \
-H "Authorization: Bearer col_ws_xxx" \
-H "Content-Type: application/json" \
-d '{
"model": "tab-v1",
"stream": true,
"tab_context": {
"surface": "code",
"workspace_slug": "acme",
"entity_id": "src/app.ts",
"document_type": "plaintext",
"code_language": "typescript",
"file_path": "src/app.ts",
"active_text_before_cursor": "export function add(a: number, b: number) {\n return ",
"active_text_after_cursor": "\n}",
"surrounding_context": "package: @acme/app"
},
"max_completion_tokens": 80
}'Streaming Response (SSE):
data: {"choices":[{"delta":{"content":" launching"}}]}
data: {"choices":[{"delta":{"content":" the"}}]}
data: {"choices":[{"delta":{"content":" new"}}]}
data: [DONE]Using the SDK
Stream a Completion
import { ColineApiClient } from '@colineapp/sdk'
const client = new ColineApiClient({
baseUrl: 'https://api.coline.app',
apiKey: process.env.COLINE_API_KEY
})
// Stream completion text
for await (const text of client.streamTabText({
tab_context: {
surface: 'notes',
workspace_slug: 'acme',
entity_id: 'note_123',
document_type: 'markdown',
active_text_before_cursor: 'Action items:\n- [ ] '
},
max_completion_tokens: 100
})) {
// Append to ghost text in your UI
editor.showGhostText(text)
}Non-Streaming Completion
const response = await client.createTabChatCompletion({
tab_context: {
surface: 'messages',
workspace_slug: 'acme',
entity_id: 'channel_456',
document_type: 'plaintext',
active_text_before_cursor: 'Can you review the '
},
max_completion_tokens: 50,
stream: false // Complete response in one request
})
const suggestion = response.choices[0].message.contentImplementation Tips
1. Debounce Input
Tab triggers after 120ms of no typing:
let debounceTimer: NodeJS.Timeout
editor.onInput(() => {
clearTimeout(debounceTimer)
debounceTimer = setTimeout(() => {
requestTabCompletion()
}, 120) // Match Tab's debounce
})2. Handle Overlapping Text
Tab may include text the user already typed. Strip duplicates:
import { sanitizeTabSuggestion } from '@colineapp/sdk'
const cleanSuggestion = sanitizeTabSuggestion({
active_text_before_cursor: 'Hello wor',
rawSuggestion: 'rld, how are you?'
})
// Returns: "ld, how are you?"3. Show Minimum Characters
Don't show suggestions shorter than 2 characters:
if (suggestion.length < 2) {
hideGhostText()
}4. Limit Context Size
Send only relevant context to minimize latency:
active_text_before_cursor: text.slice(-600), // Last 600 chars
active_text_after_cursor: text.slice(0, 80), // Next 80 chars
surrounding_context: nearbyBlocks.slice(0, 500) // 500 chars of contextConfiguration
Default Behavior
| Setting | Value |
|---|---|
| Debounce | 120ms |
| Max output tokens | 500 |
| Min visible chars | 2 |
| Temperature | 0.2 |
| Top P | Default (not set) |
Temperature
Lower = more predictable, higher = more creative:
0.0- Very conservative, stays close to your text0.2(default) - Balanced, natural completions0.5- More varied suggestions
Pricing
| Type | Price |
|---|---|
| Input | $0.20 per million tokens |
| Output | $0.50 per million tokens |
Example: A typical 50-token completion costs ~$0.000025. Super cheap.
Rate Limits
Tab shares the same rate limits as the main API:
| Auth Type | Read/Stream | Write |
|---|---|---|
| API Key | 300 / minute | 60 / minute |
| Session | 200 / minute | 40 / minute |
Surfaces Deep Dive
Any Editor
The only required idea is cursor context. Use any stable surface name that helps you segment analytics and prompt behavior:
tab_context: {
surface: 'email',
workspace_slug: 'acme',
entity_id: 'draft_123',
document_type: 'plaintext',
active_text_before_cursor: 'Hey Maya,\n\nFollowing up on ',
active_text_after_cursor: '',
surrounding_context: 'Thread subject: Launch plan'
}Code
OpenAI-compatible autocomplete for code editors:
tab_context: {
surface: 'code',
entity_id: 'src/app.ts',
document_type: 'plaintext',
code_language: 'typescript',
file_path: 'src/app.ts',
active_text_before_cursor: 'const total = ',
active_text_after_cursor: '\nconsole.log(total)'
}Notes
Works in any text block:
- Paragraphs
- Headings (H1, H2, H3)
- Lists (bulleted, numbered, todo)
- Quotes
- Callouts
tab_context: {
surface: 'notes',
block_kind: 'paragraph', // or 'heading1', 'bulleted-list', etc.
note_title: 'Meeting Notes'
}Docs
Rich text editing with formatting awareness:
tab_context: {
surface: 'docs',
document_title: 'Project Spec',
document_type: 'markdown'
}Messages
Chat completions with conversation context:
tab_context: {
surface: 'messages',
surrounding_context: '[conversation history]\n---\n[composing] '
}Tasks
Task descriptions with status/priority awareness:
tab_context: {
surface: 'tasks',
taskboard_file_id: 'file_taskboard_123'
}Ghost Text UI
Tab suggestions appear as ghost text — muted text after the cursor:
Hello wor[ld, how are you?]
↑
cursor
[ghost text in gray]Accept: Press Tab or click the suggestion Ignore: Keep typing Dismiss: Press Escape
System Prompts
Tab uses surface-specific system prompts for best results:
- Code: Continue code naturally, match language, indentation, and local style
- Notes/Docs: Continue writing naturally, match formatting
- Messages: Keep it conversational, short and punchy
- Tasks: Descriptive, actionable, matches task metadata
Error Handling
try {
for await (const text of client.streamTabText(request)) {
showSuggestion(text)
}
} catch (error) {
if (isColineApiError(error)) {
if (error.status === 429) {
// Rate limited — back off
await sleep(1000)
}
}
}Best Practices
- Always stream — Ghost text should appear progressively
- Debounce 120ms — Don't trigger on every keystroke
- Sanitize suggestions — Remove overlapping text
- Show min 2 chars — Ignore tiny suggestions
- Accept on Tab — It's literally in the name
- Dismiss on type — Keep typing = ignore suggestion
- Limit context — Send only nearby text for speed
Next Steps
- Try the Playground — Test Tab live
- SDK Reference — Full SDK documentation
- Rate Limits — API limits and best practices