Blink
Build and host full-stack apps with managed database, auth, storage, backend, queue, and custom domains — all from your editor.
blink
MCP server: blink
{
"command": "npx",
"args": [
"-y",
"@blinkdotnew/mcp"
],
"env": {
"BLINK_API_KEY": "${BLINK_API_KEY}"
}
}backend-developer
Builds the data and logic layer — database schema, edge functions, auth config, queue tasks, secrets, and integrations. Use for backend, API, and data tasks.
You are a backend developer building data and logic layers on Blink infrastructure.
## Your scope
- Database schema design and SQL migrations via `blink db query`
- Hono edge functions in `backend/index.ts` for server-side logic
- Auth provider configuration (`blink auth-config set`)
- Queue task scheduling (`blink queue enqueue`)
- Environment secrets (`blink env set`)
- Third-party integrations (Stripe, Resend, etc.) via backend routes
## What you do NOT do
- UI components, CSS, or visual design — that's the frontend developer's job
- You only edit frontend files to add data hooks (e.g., connecting `blink.db` calls)
## Backend architecture
Blink backend is a **Hono server** deployed to Cloudflare Workers for Platforms:
```typescript
// backend/index.ts
import { Hono } from 'hono'
const app = new Hono()
app.get('/api/health', (c) => c.json({ ok: true }))
app.post('/api/queue', async (c) => {
const { taskName, payload } = await c.req.json()
// Handle queue-delivered tasks here
return c.json({ received: true })
})
export default app
```
Deploy: `blink backend deploy`
## Database rules
- Use `blink_db_query` or CLI `blink db query` for schema changes (CREATE TABLE, ALTER TABLE)
- Use `blink.db.<table>` from the SDK for client-side CRUD
- SQLite: booleans are `1`/`0`, use TEXT for UUIDs, INTEGER for timestamps (unix ms)
- Always add indexes on columns used in WHERE clauses and foreign keys
## Queue tasks
Enqueue: `blink queue enqueue --destination /api/queue --payload '{"taskName":"send-email"}'`
Schedule: `blink queue schedule --cron "0 * * * *" --destination /api/queue --payload '{"taskName":"hourly-cleanup"}'`
Tasks are HTTP POST requests delivered to the backend.frontend-developer
Builds and refines the visual layer — UI components, layouts, responsive design, animations, and routing. Use for visual design work, component creation, and styling tasks.
You are a frontend developer building the visual layer on Blink infrastructure.
## Your scope
- Layout, styling, and responsive design
- UI components and animations
- Routing and navigation (React Router, file-based routing)
- Theme and design system setup
- Accessibility and visual polish
## What you do NOT do
- Database schema or queries — that's the backend developer's job
- Edge function deployment or server-side logic
- Auth provider configuration (just use the already-configured `blink.auth` in components)
## Supported frameworks
- React/Vite (primary)
- Next.js (static export only for Blink hosting)
- Vue 3 + Vite
- Svelte/SvelteKit
- Astro
- Expo React Native (mobile)
## Component library
If the project uses `@blinkdotnew/ui`, import components from there. Otherwise use shadcn/ui + Tailwind CSS.
## How to add new pages
1. Create the component in your routing folder
2. Register the route in the router config
3. Link to it from navigation
4. Run `blink deploy ./dist --prod` when ready
When you need backend data, wire up `blink.db.<table>` hooks and note what tables/columns are required so the backend developer can create them.full-stack-builder
Builds complete apps on Blink infrastructure — database, auth, backend, frontend, deploy, and custom domains. Use when the user wants to create or extend a full-stack application.
You are a full-stack developer building on Blink infrastructure. You have access to managed database (SQLite/libSQL), authentication, file storage, serverless backend (Hono on CF Workers), task queues, custom domains, and production hosting.
## How you work
1. **Create the project**: Use `blink_project_create` or `blink init --name "app-name"`
2. **Set up auth**: Enable providers with `blink_auth_set_config`, then use `@blinkdotnew/sdk` auth in the frontend
3. **Design the database**: Create tables with `blink_db_query`, use `blink.db.<table>` in client code
4. **Build the frontend**: React/Vite, Next.js (static export), Vue, Svelte, or Astro
5. **Add backend if needed**: Hono server in `backend/index.ts` for webhooks, server-side secrets, third-party callbacks
6. **Deploy**: `npm run build && blink deploy ./dist --prod`
7. **Connect domain**: `blink_domains_add` then configure DNS
## Key rules
- Initialize SDK with `projectId` and `publishableKey` from environment variables
- Use `blink.db` for CRUD — never raw SQL from client code
- Backend must export a default Hono app from `backend/index.ts`
- SQLite booleans are `1`/`0`, IDs are strings with prefix (`usr_`, `post_`)
- Store secrets with `blink_env_set`, never hardcode
- Build before deploy — the deploy command uploads pre-built static filesverifier
Reviews code for correctness, bugs, broken imports, type errors, missing implementations, integration issues, and security problems. Returns PASS or FAIL with actionable fixes.
You are a code reviewer verifying work done on a Blink-hosted project. Read all modified files end-to-end and check for issues.
## Checklist
1. **Imports**: No broken imports, no missing dependencies, no circular refs
2. **Types**: No type errors, correct TypeScript usage, proper null handling
3. **SDK usage**: `createClient()` called with `projectId` AND `publishableKey`, correct `blink.db` method signatures
4. **Auth**: Providers enabled before use, auth state checked before protected operations
5. **Database**: SQL syntax correct for SQLite, booleans as integers, IDs as strings, proper indexes
6. **Backend**: Hono server exports default, routes handle errors, secrets not hardcoded
7. **Deploy**: Build step exists, output dir matches deploy command, no dev-only code in production
8. **Security**: No secrets in client code, auth checks on protected routes, input validation on backend
## Output format
If everything passes:
```
**PASS** — All checks passed. No blocking issues found.
```
If issues found:
```
**FAIL** — Found N blocking issues:
1. [file:line] Description of issue
Fix: What to do
2. [file:line] Description of issue
Fix: What to do
```
Non-blocking suggestions (style, accessibility) go in a separate "Suggestions" section — never block on them.blink-agents
Blink Claw agent management — managed AI agent hosting on Fly.io. List agents, check status, manage secrets. For autonomous agents, Telegram/Discord bots, and scheduled workflows.
## What is Blink Claw?
Blink Claw provides **managed AI agent hosting** on Fly.io. Each agent runs in its own isolated Fly machine with persistent storage, pre-installed tools (browser, file system, CLI), and always-on connectivity for platforms like Telegram, Discord, and Slack.
## Getting Started
```bash
# List all agents in the workspace
blink agent list
# Check agent status
blink agent status <agent-id>
# Manage agent secrets
blink secrets list <agent-id>
blink secrets set <agent-id> MY_API_KEY=sk-123
blink secrets delete <agent-id> MY_API_KEY
```
## MCP Tools
| Tool | Description |
|------|-------------|
| `blink_agent_list` | List all Claw agents in the workspace |
| `blink_agent_status` | Get agent status, machine info, and health |
| `blink_secrets_list` | List secret names for an agent |
| `blink_secrets_set` | Set a secret on an agent |
| `blink_secrets_delete` | Delete a secret from an agent |
## Agent Lifecycle
Agents are created and configured via the Blink dashboard at `blink.new`. Once created:
1. **Running** — Agent machine is active, processing tasks
2. **Stopped** — Machine paused, can be resumed
3. **Error** — Machine crashed, check logs
## CLI Usage
```bash
# List agents with status
blink agent list
# Output:
# ID Name Status Platform Created
# ag_a1b2c3d4 Support Bot running telegram 2026-03-15
# ag_e5f6g7h8 Data Scraper stopped none 2026-04-01
# Get detailed status
blink agent status ag_a1b2c3d4
# Output:
# Name: Support Bot
# Status: running
# Platform: telegram
# Machine: blink-claw-a1b2c3d4 (iad)
# Uptime: 3d 14h 22m
# Memory: 512MB / 1GB
# Set environment secrets
blink secrets set ag_a1b2c3d4 OPENAI_API_KEY=sk-abc123
blink secrets set ag_a1b2c3d4 TELEGRAM_BOT_TOKEN=123456:ABC
# List secrets (values hidden)
blink secrets list ag_a1b2c3d4
# Output:
# OPENAI_API_KEY ****abc123
# TELEGRAM_BOT_TOKEN ****ET:ABC
# Remove a secret
blink secrets delete ag_a1b2c3d4 OPENAI_API_KEY
```
## Use Cases
- **Telegram/Discord/Slack bots** — always-on agents connected to messaging platforms
- **Autonomous research agents** — web browsing, data collection, report generation
- **Scheduled workflows** — periodic data processing, monitoring, alerts
- **Customer support** — AI-powered ticket triage and response
- **DevOps automation** — deployment monitoring, incident response
## Agent Architecture
Each Claw agent runs on a dedicated Fly.io machine with:
- **Persistent volume** at `/data` (workspace files, agent state)
- **Pre-installed tools**: Chromium browser, Blink CLI, Node.js
- **Networking**: public HTTPS endpoint + WebSocket gateway
- **Skills**: bundled in Docker image at `/app/skills/`
## Notes
- Agent creation/configuration is done via the Blink dashboard, not CLI
- Secrets are stored in the Fly machine's `env` config — not Fly app secrets
- Agents are billed per-hour of machine uptime
- Each agent gets its own Fly app: `blink-claw-{last8ofAgentId}`blink-ai
AI Gateway for text generation, image generation/editing, video generation, text-to-speech, audio transcription, and AI phone calls. Unified access to 50+ models.
## Getting Started
```bash
# Generate text
blink ai text "Write a poem about coding"
# Generate image
blink ai image "A sunset over mountains" --model fal-ai/nano-banana-pro
# Generate video
blink ai video "A cooking tutorial scene" --aspect-ratio 9:16
# Text-to-speech
blink ai speech "Hello world" --voice nova
# Transcribe audio
blink ai transcribe ./recording.mp3
# AI phone call
blink ai call +15551234567 --prompt "Remind about appointment tomorrow at 3pm"
```
## MCP Tools
| Tool | Description |
|------|-------------|
| `blink_ai_text` | Generate text from prompt or messages |
| `blink_ai_image` | Generate or edit images |
| `blink_ai_video` | Generate video from text or image |
| `blink_ai_speech` | Convert text to audio |
| `blink_ai_transcribe` | Convert audio to text |
| `blink_ai_call` | Make AI-powered phone call |
## SDK Methods
```typescript
const { text } = await blink.ai.generateText({ prompt: 'Hello', search: true })
const { data } = await blink.ai.generateImage({ prompt: 'A robot', model: 'fal-ai/nano-banana-pro' })
const { result } = await blink.ai.generateVideo({ prompt: 'Ocean waves', model: 'fal-ai/veo3.1/fast' })
const { url } = await blink.ai.generateSpeech({ text: 'Hello', voice: 'nova' })
const { text } = await blink.ai.transcribeAudio({ audio: audioData, language: 'en' })
```
## Text Generation
```bash
# Simple prompt
blink ai text "Summarize quantum computing"
# With web search for real-time info
blink ai text "Latest AI news today" --search
# Stream output
blink ai text "Write a story" --stream
```
```typescript
await blink.ai.streamText(
{ prompt: 'Write a story...', search: true },
(chunk) => setText(prev => prev + chunk)
)
```
## Image Models
| Model | Speed | Quality | Best For |
|-------|-------|---------|----------|
| `fal-ai/nano-banana` (default) | Fast | Good | Prototypes |
| `fal-ai/nano-banana-pro` | Standard | Excellent | Marketing, high-fidelity |
| `fal-ai/nano-banana/edit` | Fast | Good | Quick adjustments, style transfer |
| `fal-ai/nano-banana-pro/edit` | Standard | Excellent | Detailed retouching |
## Video Models
| Model | Type | Best For |
|-------|------|----------|
| `fal-ai/veo3.1/fast` (default) | T2V | Fast, good quality |
| `fal-ai/veo3.1` | T2V | Best quality |
| `fal-ai/sora-2/text-to-video/pro` | T2V | Cinematic |
| `fal-ai/veo3.1/image-to-video` | I2V | Animate images |
## Speech Voices
`alloy` (default), `echo`, `fable`, `nova`, `onyx`, `shimmer`
## Transcription Models
| Model | Notes |
|-------|-------|
| `fal-ai/whisper` (default) | Best accuracy |
| `fal-ai/wizper` | Faster, Whisper v3 optimized |
| `fal-ai/speech-to-text/turbo` | Fast, lower cost |
## Critical Rules
1. **Image URLs must be HTTPS with extension** — `.jpg`, `.png`, `.gif`, `.webp`
2. **Style transfer** — provide ALL images in array, reference by position in prompt
3. **I2V requires image upload** — upload to storage first, use public URL
4. **generateObject schema must be `type: "object"`** — wrap arrays inside object
5. **System prompts must be domain-specific** — never use "You are a helpful assistant"
## AI Phone Calls
```bash
# Outbound call with custom prompt
blink ai call +15551234567 --prompt "You are a scheduling assistant. Confirm the appointment."
# With voice selection
blink ai call +15551234567 --prompt "Remind about meeting" --voice nova
```
Requires a phone number — see `blink-notifications` skill for `blink phone buy`.blink-auth
Authentication with managed and headless modes. Social providers, email/password, magic links, RBAC.
**Important**: Enable providers via CLI or MCP before using them in your app: `blink auth-config set --provider google --enabled true`
## MCP Tools
`blink_auth_config_get` · `blink_auth_config_set`
## Getting Started
```bash
# Enable auth providers via CLI
blink auth-config set --provider google --enabled true
blink auth-config set --provider github --enabled true
blink auth-config set --provider email --enabled true
```
## Two Auth Modes
### Managed Mode (Quick Setup)
Redirects to hosted auth page. Best for websites and MVPs. **Not for mobile.**
```typescript
const blink = createClient({
projectId: import.meta.env.VITE_BLINK_PROJECT_ID || 'your-project-id',
publishableKey: import.meta.env.VITE_BLINK_PUBLISHABLE_KEY || 'blnk_pk_xxx',
auth: { mode: 'managed' },
})
blink.auth.login() // Redirects to blink.new/auth
blink.auth.login('https://app.com/dashboard') // Custom redirect after login
blink.auth.logout()
```
### Headless Mode (Custom UI)
Full control. **Required for mobile (Expo/React Native).**
```typescript
const blink = createClient({
projectId: import.meta.env.VITE_BLINK_PROJECT_ID || 'your-project-id',
publishableKey: import.meta.env.VITE_BLINK_PUBLISHABLE_KEY || 'blnk_pk_xxx',
auth: { mode: 'headless' },
})
await blink.auth.signUp({ email, password })
await blink.auth.signInWithEmail(email, password)
await blink.auth.signInWithGoogle()
await blink.auth.signInWithGitHub()
await blink.auth.signInWithApple()
await blink.auth.signInWithMicrosoft()
```
### Mode Comparison
| Feature | Managed | Headless |
| -------- | ------------- | ------------ |
| Setup | 1 line | Custom UI |
| Mobile | ❌ | ✅ Required |
| Branding | Blink-branded | Fully custom |
## Auth State Listener (Required for React)
```typescript
useEffect(() => {
const unsubscribe = blink.auth.onAuthStateChanged((state) => {
setUser(state.user)
if (!state.isLoading) setLoading(false) // Only set false, never reset to true
})
return unsubscribe
}, [])
```
**Loading state**: Always show a spinner or skeleton while loading — never `return null` (causes blank screen in production).
## Signup with Custom Fields
```typescript
await blink.auth.signUp({
email: 'user@example.com',
password: 'password123',
displayName: 'John Doe',
role: 'editor',
metadata: { company: 'Acme Inc' },
})
```
## Password Reset
```typescript
await blink.auth.sendPasswordResetEmail(email)
await blink.auth.confirmPasswordReset(token, newPassword)
// Custom flow: generate token, send your own email
const { token, resetUrl } = await blink.auth.generatePasswordResetToken(email)
```
## Magic Links
```typescript
await blink.auth.sendMagicLink(email)
await blink.auth.verifyMagicLink(token)
```
## RBAC (Role-Based Access)
```typescript
const blink = createClient({
projectId: import.meta.env.VITE_BLINK_PROJECT_ID || 'your-project-id',
publishableKey: import.meta.env.VITE_BLINK_PUBLISHABLE_KEY || 'blnk_pk_xxx',
auth: {
mode: 'headless',
roles: {
admin: { permissions: ['*'] },
editor: { permissions: ['posts.create', 'posts.update'], inherit: ['viewer'] },
viewer: { permissions: ['posts.read'] },
},
},
})
blink.auth.can('posts.update') // Check permission
blink.auth.hasRole('admin') // Check role
```
## Core Methods
```typescript
const user = await blink.auth.me()
await blink.auth.updateMe({ displayName: 'New Name' })
const token = await blink.auth.getValidToken()
await blink.auth.signOut()
```
## How Auth Interacts with Blink APIs
When signed in, the SDK attaches `Authorization: Bearer <JWT>` automatically. Most modules require auth. Public exceptions: analytics ingest, storage upload (add-only).
## Expo/React Native Setup
```typescript
import * as WebBrowser from 'expo-web-browser'
import AsyncStorage from '@react-native-async-storage/async-storage'
import { createClient, AsyncStorageAdapter } from '@blinkdotnew/sdk'
const blink = createClient({
projectId: process.env.EXPO_PUBLIC_BLINK_PROJECT_ID!,
publishableKey: process.env.EXPO_PUBLIC_BLINK_PUBLISHABLE_KEY!,
auth: { mode: 'headless', webBrowser: WebBrowser },
storage: new AsyncStorageAdapter(AsyncStorage),
})
```blink-backend
Blink Backend — Hono server on CF Workers for webhooks, server-side secrets, custom APIs. Pro+ only. Deploy via CLI.
## MCP Tools
`blink_backend_status` · `blink_backend_deploy` · `blink_backend_logs`
## When to Use
- **Prefer SDK** for auth, db, storage, AI (~95% of cases)
- **Use backend** for: webhooks, server-side secrets, third-party callbacks, heavy processing
- **Never** create a backend just for CRUD — use `blink.db` instead
## Getting Started
```bash
# Check backend eligibility (Pro/Max/Team)
blink backend status
# Create the entry point
mkdir -p backend
# Write backend/index.ts (see below)
# Deploy
blink backend deploy
# View logs
blink backend logs
```
## Structure
```
backend/
├── index.ts ← REQUIRED: export default Hono app
├── routes/
│ ├── stripe.ts
│ └── webhooks.ts
└── lib/
└── auth.ts
```
## `backend/index.ts` (Required Entry Point)
```typescript
import { Hono } from "hono"
import { cors } from "hono/cors"
import { createClient } from "@blinkdotnew/sdk"
const app = new Hono()
app.use("*", cors())
const getBlink = (env: Record<string, string>) =>
createClient({
projectId: env.BLINK_PROJECT_ID,
secretKey: env.BLINK_SECRET_KEY, // server key, not publishableKey
})
app.get("/health", (c) => c.json({ ok: true }))
app.post("/api/example", async (c) => {
const blink = getBlink(c.env as Record<string, string>)
const body = await c.req.json()
return c.json({ success: true })
})
export default app
```
## Secrets
**Auto-injected** (never hardcode): `BLINK_PROJECT_ID`, `BLINK_SECRET_KEY`, `BLINK_PUBLISHABLE_KEY`
**User secrets** (third-party keys): Add via project settings or CLI, access as `c.env.MY_SECRET`.
## JWT Verification
```typescript
app.post("/api/protected", async (c) => {
const blink = getBlink(c.env as Record<string, string>)
const auth = await blink.auth.verifyToken(c.req.header("Authorization"))
if (!auth.valid) return c.json({ error: auth.error }, 401)
return c.json({ userId: auth.userId })
})
```
## Calling from Frontend
```typescript
// Direct fetch
const res = await fetch("https://{projectId8}.backend.blink.new/api/example", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ key: "value" }),
})
// Via SDK
const data = await blink.functions.invoke("api/example", { body: { key: "value" } })
```
`projectId8` = last 8 chars of the full project ID.
## Deployment Flow
```bash
# 1. Check eligibility
blink backend status
# 2. Write backend/index.ts
# 3. Deploy (reads all backend/ files, bundles with esbuild, deploys to CF Workers)
blink backend deploy
# 4. URL is printed: https://{projectId8}.backend.blink.new
```
## Common Errors
| Error | Cause | Fix |
| ---------------------- | ---------------------- | ------------------------------------- |
| `BACKEND_REQUIRES_PRO` | Free/Starter workspace | Upgrade to Pro+ |
| `MISSING_ENTRYPOINT` | No `backend/index.ts` | Create with `export default app` |
| CORS error | Missing middleware | Add `app.use("*", cors())` |
| 401 on blink-apis | Wrong key | Use `secretKey`, not `publishableKey` |
## Checklist
- `backend/index.ts` exports `default app`
- `app.use("*", cors())` present
- Uses `secretKey` in `createClient` (not publishableKey)
- Deployed with `blink backend deploy`
- AbortController timeout on all external `fetch()` callsblink-connectors
OAuth connector system for 38+ third-party services. Execute API calls to Google, Notion, Slack, Discord, GitHub, Stripe, Jira, HubSpot, Salesforce, LinkedIn, and more.
## Getting Started
```bash
# List available providers
blink connector providers
# Check connection status
blink connector status slack
# Execute an API call
blink connector exec slack /conversations GET
```
## MCP Tools
| Tool | Description |
|------|-------------|
| `blink_connector_exec` | Execute API call through a connected provider |
| `blink_connector_providers` | List all available connector providers |
| `blink_connector_status` | Check if a provider is connected |
## Prerequisites
**Initial OAuth connection must be done in the browser** at `blink.new/settings?tab=connectors`. Users authorize each provider once, then CLI/MCP/SDK can make API calls.
## SDK Usage
```typescript
// Check status
const status = await blink.connectors.status('slack')
// GET request
const channels = await blink.connectors.execute('slack', {
method: '/conversations',
http_method: 'GET',
params: { types: 'public_channel,private_channel' }
})
// POST request
await blink.connectors.execute('slack', {
method: '/chat/postMessage',
http_method: 'POST',
params: { channel: 'C1234', text: 'Hello from Blink!' }
})
```
## Available Providers
| Provider | ID | Common Endpoints |
|----------|-----|-----------------|
| **Slack** | `slack` | `/conversations`, `/chat/postMessage`, `/users` |
| **Discord** | `discord` | `/user`, `/guilds`, `/channels/{id}/messages` |
| **Notion** | `notion` | `/search`, `/databases`, `/pages` |
| **Google Drive** | `google_drive` | `/files` |
| **Google Calendar** | `google_calendar` | `/events` |
| **Google Sheets** | `google_sheets` | `/spreadsheets/{id}/values/{range}` |
| **Google Docs** | `google_docs` | `/documents` |
| **Google Slides** | `google_slides` | `/presentations` |
| **LinkedIn** | `linkedin` | `/userinfo`, `/ugcPosts` |
| **HubSpot** | `hubspot` | `/contacts`, `/companies`, `/deals` |
| **Salesforce** | `salesforce` | `/query`, `/sobjects/{type}` |
| **GitHub** | `github` | `/user`, `/repos`, `/issues` |
| **Stripe** | `stripe` | `/customers`, `/charges`, `/subscriptions` |
| **Jira** | `jira` | `/search`, `/issue`, `/project` |
| **Microsoft** | `microsoft` | `/me`, `/messages`, `/events` |
| **Airtable** | `airtable` | `/bases`, `/records` |
Plus 20+ more — run `blink connector providers` for the full list.
## CLI Examples
```bash
# Slack: list channels
blink connector exec slack /conversations GET
# Notion: search pages
blink connector exec notion /search POST '{"query":"meeting notes"}'
# Google Sheets: read data
blink connector exec google_sheets "/spreadsheets/SHEET_ID/values/Sheet1!A1:D10" GET
# HubSpot: list contacts
blink connector exec hubspot /contacts GET '{"limit":50}'
# Salesforce: SOQL query
blink connector exec salesforce /query GET '{"q":"SELECT Id,Name FROM Account LIMIT 10"}'
```
**CLI syntax**: `blink connector exec <provider> <endpoint> [method] [params]`
## Method Path Rules
1. **Paths start with `/`** — `/conversations` not `conversations`
2. **Dynamic IDs go in the path** — `/channels/${channelId}/messages`
3. **Provider IDs use underscores** — `google_drive`, `google_calendar`, `google_sheets`
4. **GET params** become query parameters; **POST params** become JSON body
## Error Codes
| Code | Meaning |
|------|---------|
| `CONNECTOR_NOT_CONNECTED` | User hasn't authorized this provider |
| `CONNECTOR_DISABLED` | Provider disabled for this project |
| `MISSING_SCOPE` | Reconnect with additional permissions |
| `*_API_ERROR` | Upstream API error (e.g. `SLACK_API_ERROR`) |
Token refresh is handled automatically by Blink backend.blink-database
Database CRUD with automatic camelCase conversion. Create, list, update, delete, upsert, count, exists. Raw SQL via CLI. Boolean handling for SQLite.
## MCP Tools
`blink_db_query` · `blink_db_schema`
## Getting Started
```bash
# Create a table
blink db query "CREATE TABLE todos (id TEXT PRIMARY KEY, title TEXT NOT NULL, user_id TEXT, is_completed INTEGER DEFAULT 0, created_at TEXT DEFAULT (datetime('now')))"
# Inspect schema
blink db query "SELECT name FROM sqlite_master WHERE type='table'"
# Run any SQL
blink db query "SELECT * FROM todos LIMIT 10"
```
## Automatic Case Conversion
SDK converts camelCase ↔ snake_case automatically. **Always use camelCase in code.**
```typescript
// ✅ Correct
await blink.db.emailDrafts.create({ userId: user.id, createdAt: new Date() })
// ❌ Wrong — never use snake_case in SDK calls
await blink.db.email_drafts.create({ user_id: user.id })
```
## CRUD Operations
```typescript
// Create (ID auto-generates as string)
const todo = await blink.db.todos.create({ title: 'Learn Blink', userId: user.id, isCompleted: false })
// Get by ID
const todo = await blink.db.todos.get('todo_12345')
// List with filtering
const todos = await blink.db.todos.list({
where: { AND: [{ userId: user.id }, { OR: [{ status: 'open' }, { priority: 'high' }] }] },
orderBy: { createdAt: 'desc' },
limit: 20,
offset: 0,
})
// Update
await blink.db.todos.update('todo_12345', { isCompleted: true })
// Delete
await blink.db.todos.delete('todo_12345')
// Upsert
await blink.db.todos.upsert({ id: 'todo_12345', title: 'Updated', userId: user.id })
// Count / Exists
const count = await blink.db.todos.count({ where: { userId: user.id } })
const exists = await blink.db.todos.exists({ where: { id: 'todo_12345' } })
```
### Batch Operations
```typescript
await blink.db.todos.createMany([{ title: 'Task 1', userId: user.id }, { title: 'Task 2', userId: user.id }])
await blink.db.todos.updateMany([{ id: 'todo_1', isCompleted: true }, { id: 'todo_2', isCompleted: true }])
await blink.db.todos.deleteMany({ where: { userId: user.id, isCompleted: "1" } })
```
## Boolean Handling (CRITICAL)
SQLite returns booleans as `"0"`/`"1"` strings, not `true`/`false`.
```typescript
// ✅ Correct
const completed = todos.filter(todo => Number(todo.isCompleted) > 0)
// Filter in queries
const done = await blink.db.todos.list({ where: { isCompleted: "1" } })
```
## TypeScript
```typescript
interface Todo {
id: string // MUST be string, never number
title: string
isCompleted: boolean // Returned as "0"/"1" from SQLite
userId: string
createdAt: string
}
// Typed queries
const todos = await blink.db.table<Todo>('todos').list()
```
## Filtering Options
| Option | Type | Example |
|--------|------|---------|
| `where` | object | `{ userId: 'abc', status: 'active' }` |
| `orderBy` | object | `{ createdAt: 'desc' }` |
| `limit` | number | `20` |
| `offset` | number | `0` |
| `select` | array | `['id', 'title']` |
## Auth & RLS
REST CRUD is JWT-protected by default — initialize with auth for user-scoped data. RLS enforces per-user access server-side.
```typescript
const blink = createClient({
projectId: process.env.NEXT_PUBLIC_BLINK_PROJECT_ID,
publishableKey: process.env.NEXT_PUBLIC_BLINK_PUBLISHABLE_KEY,
auth: { mode: 'managed' },
})
```
## Async Operations Warning
Never create a DB record before an async operation (AI, upload) completes — nullable fields or missing data will fail NOT NULL constraints. Create records only **after** you have all data.blink-deploy
Build and deploy Blink apps to production. Preview vs production deploys, deploy pipeline, static site hosting.
## MCP Tools
`blink_deploy` · `blink_deployments_list` · `blink_rollback`
## Getting Started
```bash
# Build your app
npm run build
# Deploy to production
blink deploy ./dist --prod
# Preview deploy (temporary URL)
blink deploy ./dist
# List deployments
blink deployments
# Rollback to a previous deployment
blink rollback <deployment_id>
```
## Deploy Pipeline
```
1. npm run build → generates ./dist (or .next, out/, build/)
2. blink deploy ./dist → uploads to Blink hosting
3. URL printed → {projectId}.sites.blink.new (or custom domain)
```
## Preview vs Production
| Flag | Behavior | URL |
|------|----------|-----|
| (none) | Preview deploy | Temporary preview URL |
| `--prod` | Production deploy | `{projectId}.sites.blink.new` + custom domains |
```bash
# Preview — test before going live
blink deploy ./dist
# → https://preview-abc123.sites.blink.new
# Production — replaces live site
blink deploy ./dist --prod
# → https://{projectId}.sites.blink.new
```
## Framework Build Outputs
| Framework | Build Command | Output Dir |
|-----------|--------------|------------|
| React (Vite) | `vite build` | `./dist` |
| Next.js (`output: 'export'`) | `next build` | `./out` |
| Vue | `vite build` | `./dist` |
| Svelte | `vite build` | `./build` |
| Astro | `astro build` | `./dist` |
| Plain HTML/CSS/JS | — | `./` |
For Next.js static export, ensure `next.config.ts` has `output: 'export'`.
## Backend Deploy
Backend (Hono on CF Workers) has its own deploy command — see `blink-backend` skill.
```bash
blink backend deploy
```
## Full Production Checklist
```bash
# 1. Ensure env vars are set
# 2. Build
npm run build
# 3. Deploy frontend
blink deploy ./dist --prod
# 4. Deploy backend (if applicable)
blink backend deploy
# 5. Set up custom domain (optional)
blink domains add myapp.com
```
## Common Issues
| Issue | Fix |
|-------|-----|
| Empty deploy | Check build output directory exists and has files |
| 404 after deploy | Verify correct output dir (`dist/`, `out/`, `build/`) |
| Env vars missing | Set secrets in project settings before build |
| Stale deploy | Ensure `--prod` flag for production updates |blink-domains
Custom domain management. Add domains, DNS setup, SSL verification, domain search, and domain purchase via CLI.
## MCP Tools
`blink_domains_add` · `blink_domains_list` · `blink_domains_verify` · `blink_domains_remove` · `blink_domains_search` · `blink_domains_purchase`
## Getting Started
```bash
# Add a custom domain to your project
blink domains add myapp.com
# Check domain status and DNS instructions
blink domains list
# Verify DNS is configured (domain_id from `blink domains list` or `blink domains add`)
blink domains verify <domain_id>
```
## Adding a Custom Domain
```bash
blink domains add myapp.com
```
The CLI outputs DNS records you need to configure:
```
Configure these DNS records at your registrar:
Type Name Value
CNAME @ cname.blink.new
CNAME www cname.blink.new
After configuring DNS, run: blink domains verify <domain_id>
```
## DNS Configuration
| Domain Type | Record Type | Name | Value |
|-------------|-------------|------|-------|
| Apex (`myapp.com`) | CNAME or A | `@` | `cname.blink.new` |
| Subdomain (`www`) | CNAME | `www` | `cname.blink.new` |
| Wildcard | CNAME | `*` | `cname.blink.new` |
**Apex + www**: Add both records. Blink auto-configures SSL for both.
## SSL Verification
SSL certificates are provisioned automatically after DNS propagation. Verify status:
```bash
# domain_id comes from `blink domains list` or `blink domains add` response
blink domains verify <domain_id>
# → SSL: active | pending | error
```
DNS propagation can take 1–48 hours. Most providers propagate within 10 minutes.
## Domain Search & Purchase
```bash
# Search for available domains
blink domains search myapp
# Purchase a domain (if available)
blink domains purchase myapp.com
```
Purchased domains are auto-configured — no manual DNS needed.
## Managing Domains
```bash
# List all domains on current project
blink domains list
# Remove a domain (use domain_id, not domain name)
blink domains remove <domain_id>
```
## Apex → www Redirect
For apex domains that can't use CNAME records (some registrars), Blink supports automatic apex-to-www redirects:
```bash
blink domains add myapp.com --redirect-www
```
This stores a redirect rule so `myapp.com` → `www.myapp.com` via 301.
## Full Setup Flow
```bash
# 1. Deploy your app first
blink deploy ./dist --prod
# 2. Add domain
blink domains add myapp.com
# 3. Configure DNS at registrar (follow output instructions)
# 4. Verify (use domain_id from step 2)
blink domains verify <domain_id>
# 5. Done — site is live at myapp.com with SSL
```
## Common Issues
| Issue | Fix |
|-------|-----|
| SSL pending | Wait for DNS propagation (up to 48h) |
| CNAME conflict | Remove existing A/AAAA records for the same name |
| Apex CNAME not supported | Use A record or enable www redirect |
| Domain not resolving | Verify records with `dig myapp.com` |blink-full-stack
End-to-end guide for building and shipping a Blink app. Project setup, SDK init, auth, database, backend, deploy, and custom domains. Index to all other skills.
## Overview
This is the master guide for building a full-stack Blink app from zero to production. Each section links to a deeper skill.
## Step 1 — Project Setup
```bash
# Create project via CLI
blink init my-app --template next
# Install SDK
cd my-app && bun add @blinkdotnew/sdk
```
## Step 2 — SDK Initialization
```typescript
import { createClient } from '@blinkdotnew/sdk'
export const blink = createClient({
projectId: process.env.NEXT_PUBLIC_BLINK_PROJECT_ID || 'your-project-id',
publishableKey: process.env.NEXT_PUBLIC_BLINK_PUBLISHABLE_KEY || 'blnk_pk_xxx',
auth: { mode: 'managed' },
})
```
Env var prefixes by framework: `NEXT_PUBLIC_` (Next.js), `VITE_` (Vite), `EXPO_PUBLIC_` (Expo).
**Why fallbacks?** If env vars are undefined, the SDK fails silently and the app hangs.
## Step 3 — Authentication
```typescript
blink.auth.login() // managed mode — redirects to hosted auth
// OR headless mode for custom UI:
await blink.auth.signInWithEmail(email, password)
```
→ Full details: see `blink-auth` skill
## Step 4 — Database
```bash
# Create tables via CLI
blink db query "CREATE TABLE todos (id TEXT PRIMARY KEY, title TEXT NOT NULL, user_id TEXT, is_completed INTEGER DEFAULT 0, created_at TEXT DEFAULT (datetime('now')))"
```
```typescript
const todos = await blink.db.todos.list({
where: { userId: user.id },
orderBy: { createdAt: 'desc' },
})
```
→ Full details: see `blink-database` skill
## Step 5 — Backend (Pro+)
Write `backend/index.ts` with a Hono server for webhooks, secrets, server logic.
```bash
blink backend deploy
```
→ Full details: see `blink-backend` skill
## Step 6 — Deploy
```bash
npm run build
blink deploy ./dist --prod
```
→ Full details: see `blink-deploy` skill
## Step 7 — Custom Domain
```bash
blink domains add myapp.com
# Follow DNS instructions from output
blink domains verify myapp.com
```
→ Full details: see `blink-domains` skill
## Key Rules
1. **camelCase everywhere** in SDK code — `userId`, `createdAt` (auto-converts to snake_case)
2. **Boolean values** from SQLite are `"0"`/`"1"` strings — use `Number(val) > 0`
3. **IDs are strings**, never numbers — `"todo_abc123"` format
4. **Auth required** for most SDK modules — set up auth before DB/storage calls
## Skill Index
| Skill | Use When |
|-------|----------|
| `blink-auth` | Login, signup, social providers, RBAC |
| `blink-database` | CRUD operations, filtering, raw SQL |
| `blink-storage` | File upload, download, management |
| `blink-backend` | Webhooks, server secrets, custom APIs |
| `blink-queue` | Background tasks, cron jobs |
| `blink-deploy` | Build and deploy to production |
| `blink-domains` | Custom domains, DNS, SSL |
| `blink-ai` | Text/image/video generation, TTS, transcription, AI calls |
| `blink-agents` | Managed AI agent hosting (Blink Claw) |
| `blink-connectors` | OAuth integrations (Slack, Google, Notion, 38+ services) |
| `blink-rag` | Knowledge base, vector search, AI Q&A with citations |
| `blink-realtime` | WebSocket pub/sub, presence, message history |
| `blink-notifications` | Email, SMS, phone numbers |
## Supported Frameworks
**Frontend**: React (Vite), Next.js (static export), Vue, Svelte, Astro, Expo React Native, plain HTML/CSS/JS
**Backend**: TypeScript/JavaScript only (Hono on CF Workers)blink-notifications
Send emails (HTML/text, attachments, cc/bcc), SMS messages, and manage phone numbers for AI calling. Platform-provided — no API keys needed.
## Getting Started
```bash
# Send email
blink notify email user@example.com --subject "Welcome!" --html "<h1>Hello</h1>"
# Send SMS
blink sms send +15551234567 "Your code is 123456"
# List phone numbers
blink phone list
# Buy a phone number
blink phone buy --area-code 415
```
## MCP Tools
| Tool | Description |
|------|-------------|
| `blink_notify_email` | Send email with HTML/text, attachments, cc/bcc |
| `blink_sms_send` | Send SMS message |
| `blink_phone_list` | List owned phone numbers |
| `blink_phone_buy` | Purchase a phone number |
## Email
No setup required — built into Blink. From address auto-generated as `noreply@{projectId}.blink-email.com`.
```typescript
const { success, messageId } = await blink.notifications.email({
to: 'customer@example.com',
subject: 'Order shipped!',
html: '<h1>Your order is on the way</h1>',
text: 'Your order is on the way',
})
```
### Full Options
```typescript
await blink.notifications.email({
to: ['team@example.com', 'manager@example.com'],
replyTo: 'support@mycompany.com',
cc: 'accounting@mycompany.com',
bcc: 'archive@mycompany.com',
subject: 'Invoice #12345',
html: '<h2>Invoice Ready</h2><p>See attached.</p>',
text: 'Invoice Ready. See attached.',
attachments: [{
url: 'https://storage.example.com/invoices/12345.pdf',
filename: 'Invoice-12345.pdf',
type: 'application/pdf'
}]
})
```
### Email Options
| Option | Type | Required | Description |
|--------|------|----------|-------------|
| `to` | string \| string[] | Yes | Recipient(s) |
| `subject` | string | Yes | Subject line |
| `html` | string | Yes* | HTML content |
| `text` | string | Yes* | Plain text fallback |
| `replyTo` | string | No | Reply-to address |
| `cc` | string \| string[] | No | CC recipients |
| `bcc` | string \| string[] | No | BCC recipients |
| `attachments` | array | No | File attachments (url + filename) |
*Either `html` or `text` required (both recommended for deliverability)
## SMS
```bash
blink sms send +15551234567 "Your verification code is 847291"
```
```typescript
await blink.notifications.sms({
to: '+15551234567',
body: 'Your verification code is 847291'
})
```
## Phone Numbers
Phone numbers are required for AI calling (see `blink-ai` skill) and SMS.
```bash
# List available numbers
blink phone list
# Buy a number in a specific area code
blink phone buy --area-code 415
# Buy any available number
blink phone buy
```
## Best Practices
1. **Always include both HTML and text** for email deliverability
2. **Use `replyTo`** instead of custom `from` address
3. **Validate email format** before sending
4. **Phone numbers use E.164 format** — `+1` prefix for US numbersblink-queue
Background task queue and cron schedules. Enqueue tasks, named FIFO queues with parallelism, auto-retry. Requires Blink Backend (Pro+).
## MCP Tools
`blink_queue_enqueue` · `blink_queue_schedule` · `blink_queue_list` · `blink_queue_stats` · `blink_queue_cancel` · `blink_queue_get` · `blink_queue_retry_dead` · `blink_queue_create_queue`
## Getting Started
```bash
# Check backend is deployed (required for queue delivery)
blink backend status
# Enqueue a task
blink queue enqueue send-welcome-email --payload '{"to":"user@example.com"}'
# Create a cron schedule
blink queue schedule create daily-digest "0 9 * * *" --payload '{"reportType":"daily"}'
# List tasks
blink queue list --status pending
# View stats
blink queue stats
```
**Min SDK: `@blinkdotnew/sdk >= 2.5.0`**
## Critical Rules
1. **Backend required** — deploy backend first; queue delivery fails without it
2. **Write handler first** — add `POST /api/queue` to `backend/index.ts` BEFORE enqueueing
3. **Schedule name = taskName** — handler receives schedule `name` as `taskName`
4. **Deploy after handler changes** — `blink backend deploy`
5. **Return 2xx** — non-2xx triggers retry, then dead-letter queue
## Step 1 — Add Handler to `backend/index.ts`
```typescript
app.post('/api/queue', async (c) => {
const { taskName, payload } = await c.req.json()
switch (taskName) {
case 'send-welcome-email':
await sendEmail(payload.to, payload.displayName)
return c.json({ ok: true })
case 'daily-digest':
await generateDigest(payload.reportType)
return c.json({ ok: true })
default:
return c.json({ error: `Unknown task: ${taskName}` }, 400)
}
})
```
**Payload by source:**
| Source | Delivered body |
|--------|---------------|
| `blink.queue.enqueue(taskName, payload)` | `{ taskName, taskId, payload }` |
| `blink.queue.schedule(name, cron, payload)` | `{ taskName: name, payload }` (no taskId) |
## Step 2 — Deploy, Then Enqueue
```bash
blink backend deploy
blink queue enqueue send-welcome-email --payload '{"to":"user@example.com"}' --queue emails --delay 10s
```
## SDK Methods
```typescript
// Enqueue
await blink.queue.enqueue('send-welcome-email', { to: 'user@example.com' })
await blink.queue.enqueue('send-welcome-email', { to: 'user@example.com' }, {
queue: 'emails', delay: '5m', retries: 3,
})
// Schedule — name IS the taskName
await blink.queue.schedule('daily-digest', '0 9 * * *', { type: 'daily' })
await blink.queue.schedule('daily-digest', '0 9 * * *', { type: 'daily' }, {
timezone: 'America/New_York',
})
// Manage schedules
await blink.queue.pauseSchedule('daily-digest')
await blink.queue.resumeSchedule('daily-digest')
await blink.queue.deleteSchedule('daily-digest')
// Named queues (create once, idempotent)
await blink.queue.createQueue('emails', { parallelism: 10 })
// Inspect & manage
const tasks = await blink.queue.list({ status: 'pending', queue: 'emails' })
await blink.queue.cancel(taskId)
const dead = await blink.queue.listDead()
await blink.queue.retryDead(taskId)
const stats = await blink.queue.stats()
```
## Cron Patterns
| Cron | When |
|------|------|
| `0 9 * * *` | Daily at 9am |
| `0 * * * *` | Every hour |
| `*/15 * * * *` | Every 15 minutes |
| `0 9 * * 1` | Every Monday at 9am |
## Common Errors
| Error | Cause | Fix |
|-------|-------|-----|
| Tasks not delivering | Backend not deployed | `blink backend deploy` |
| `Unknown task: X` | Missing case in handler | Add matching case |
| Tasks retrying | Handler returning non-2xx | Return `c.json({ ok: true })` |
| `cancel` returns 409 | Task not pending | Can only cancel `'pending'` tasks |
## Billing
1 credit = 1,000 tasks (0.001 credits/task). Retries and reads are free. Pro+ only.blink-rag
Knowledge base with vector search, document upload, collections, and AI-powered Q&A with citations. Semantic search over uploaded documents.
## Getting Started
```bash
# Search a collection
blink rag search docs "How do I configure auth?"
# Upload a document
blink rag upload docs ./guide.txt
# List collections
blink rag collections
```
## MCP Tools
| Tool | Description |
|------|-------------|
| `blink_rag_search` | Semantic search or AI Q&A over a collection |
| `blink_rag_collections` | List available collections |
## SDK Methods
```typescript
// Create collection
const col = await blink.rag.createCollection({ name: 'docs', description: 'Product docs' })
// Upload document
const doc = await blink.rag.upload({
collectionName: 'docs',
filename: 'guide.txt',
content: 'Your content...',
})
await blink.rag.waitForReady(doc.id)
// Vector search
const results = await blink.rag.search({
collectionName: 'docs',
query: 'How do I configure auth?',
maxResults: 5,
})
// AI search (RAG) — returns answer + sources
const result = await blink.rag.aiSearch({
collectionName: 'docs',
query: 'What are the main features?',
model: 'google/gemini-3-flash',
})
console.log(result.answer, result.sources)
```
## Upload Methods
```typescript
// Text content
await blink.rag.upload({ collectionName: 'docs', filename: 'notes.txt', content: 'Text...' })
// From URL
await blink.rag.upload({ collectionName: 'docs', filename: 'article.html', url: 'https://example.com/article' })
// File (base64)
await blink.rag.upload({ collectionName: 'docs', filename: 'report.pdf', file: { data: base64, contentType: 'application/pdf' } })
```
## Critical: PDF Upload Pattern
**Do NOT upload PDFs directly as base64** — embeddings may store incorrectly. Extract text first:
```typescript
// 1. Upload PDF to storage
const { publicUrl } = await blink.storage.upload(pdfFile, `docs/${Date.now()}_${pdfFile.name}`)
// 2. Extract text
const text = await blink.data.extractFromUrl(publicUrl)
// 3. Upload extracted text as content
await blink.rag.upload({ collectionName: 'docs', filename: pdfFile.name, content: text })
```
## Document Lifecycle
Documents go through: `pending` → `processing` → `ready` (or `error`). Processing takes 30-40 seconds. Always wait before searching:
```typescript
const doc = await blink.rag.upload({ ... })
await blink.rag.waitForReady(doc.id, { timeoutMs: 120000 })
```
## Streaming AI Search
```typescript
const stream = await blink.rag.aiSearch({
collectionName: 'docs',
query: 'Explain the architecture',
stream: true,
})
```
## Model Selection
**Always use `google/gemini-3-flash`** for RAG AI search. Deprecated models will fail.
## Common Errors
| Error | Fix |
|-------|-----|
| 409 "Collection exists" | Catch and reuse existing collection |
| Zero tokens in search | Document still processing — wait for `ready` status |
| "Identical content" on upload | Duplicate doc — catch error, use existing doc ID |blink-realtime
WebSocket pub/sub messaging with channels, presence tracking, and message history. Real-time communication for chat, collaboration, and live updates.
**Requires auth**: initialize with `auth: { mode: 'managed' }` for realtime to work.
## Getting Started
```bash
# Publish a message to a channel
blink realtime publish chat-room --type message --data '{"text":"Hello everyone!"}'
# Publish with user context
blink realtime publish notifications --type alert --data '{"level":"info","text":"Deploy complete"}'
```
## MCP Tools
| Tool | Description |
|------|-------------|
| `blink_realtime_publish` | Publish event to a channel |
## SDK Methods
```typescript
// Simple subscribe/publish
const unsubscribe = await blink.realtime.subscribe('chat-room', (message) => {
console.log(message.data)
})
await blink.realtime.publish('chat-room', 'message', { text: 'Hello!' })
unsubscribe()
```
## Channel API (Advanced)
```typescript
const channel = blink.realtime.channel('game-lobby')
await channel.subscribe({
userId: user.id,
metadata: { displayName: user.name, status: 'online' }
})
channel.onMessage((msg) => {
if (msg.type === 'chat') addMessage(msg.data)
})
channel.onPresence((users) => {
setOnlineUsers(users)
})
await channel.publish('chat', { text: 'Hello!' }, { userId: user.id })
const history = await channel.getMessages({ limit: 50 })
const users = await channel.getPresence()
await channel.unsubscribe()
```
## Message Format
```typescript
{
id: '1640995200000-0',
type: 'chat',
data: { text: 'Hello!' },
timestamp: 1640995200000,
userId: 'user123',
metadata: { displayName: 'John' }
}
```
## Presence Format
```typescript
{
userId: 'user123',
metadata: { displayName: 'John', status: 'online' },
joinedAt: 1640995200000,
lastSeen: 1640995230000
}
```
## React Cleanup (Critical)
```typescript
useEffect(() => {
if (!user?.id) return
let channel: any = null
const init = async () => {
channel = blink.realtime.channel('room')
await channel.subscribe({ userId: user.id })
channel.onMessage((msg: any) => setMessages(prev => [...prev, msg]))
}
init().catch(console.error)
return () => { channel?.unsubscribe() }
}, [user?.id])
```
**Never** return cleanup from inside an async function — it gets lost. Store channel reference outside and clean up synchronously.
## Use Cases
- **Chat apps** — messages + presence + history
- **Live collaboration** — cursor positions, document edits
- **Notifications** — real-time alerts pushed to connected clients
- **Gaming** — game state sync, lobby management
- **Dashboards** — live data updatesblink-storage
File upload with progress tracking and public URLs. Download, remove files. CLI for management. Extension auto-detection.
## Getting Started
```bash
# Upload a file
blink storage upload ./photo.jpg --path uploads/photo.jpg
# List files
blink storage list
# List files in a path
blink storage list uploads/
```
## Auth Requirements
| Operation | Auth Required | Notes |
| ------------ | ------------- | ------------------------------ |
| `upload()` | ❌ No | Public add-only (no overwrite) |
| `download()` | ✅ Yes | Signed URL |
| `remove()` | ✅ Yes | Requires JWT |
## Upload
```typescript
const { publicUrl } = await blink.storage.upload(
file,
`uploads/${Date.now()}.${file.name.split('.').pop()}`,
{ onProgress: (percent) => console.log(`${percent}%`) }
)
```
SDK auto-detects file type from content and corrects the extension. Uploads are **add-only** — duplicate paths return `409`. Use timestamps or random IDs for uniqueness.
## Download
```typescript
const { downloadUrl, filename } = await blink.storage.download('images/photo.jpg')
window.open(downloadUrl, '_blank')
// Custom filename
const { downloadUrl } = await blink.storage.download('images/photo.jpg', {
filename: 'my-photo.jpg',
})
```
## Remove
```typescript
await blink.storage.remove('uploads/file1.jpg')
await blink.storage.remove('file1.jpg', 'file2.jpg', 'file3.png') // multiple
```
## Common Patterns
### Avatar Upload
```typescript
const handleAvatarUpload = async (file: File) => {
const { publicUrl } = await blink.storage.upload(
file,
`avatars/${user.id}-${Date.now()}.${file.name.split('.').pop()}`
)
await blink.auth.updateMe({ avatar: publicUrl })
return publicUrl
}
```
### Upload with Progress
```typescript
const [progress, setProgress] = useState(0)
const handleUpload = async (file: File) => {
const { publicUrl } = await blink.storage.upload(
file,
`documents/${Date.now()}.${file.name.split('.').pop()}`,
{ onProgress: (percent) => setProgress(percent) }
)
return publicUrl
}
```
### For AI Image Input
Upload first to get an HTTPS URL with extension — required for AI vision.
```typescript
const { publicUrl } = await blink.storage.upload(photo, `photos/${Date.now()}.jpg`)
const { text } = await blink.ai.generateText({
messages: [{ role: "user", content: [
{ type: "text", text: "What's in this image?" },
{ type: "image", image: publicUrl },
]}],
})
```
## URL Format
Uploaded files return direct HTTPS URLs: `https://storage.googleapis.com/{project}/uploads/{filename}.{ext}` — publicly accessible, HTTPS, with file extension.
## Common Errors
| Error | Cause | Fix |
| -------------- | ------------------- | ----------------------------------------------------- |
| 409 conflict | Path already exists | Add timestamp/random ID |
| Invalid path | Special characters | Sanitize: `path.replace(/[^a-zA-Z0-9_\-\/\.]/g, '_')` |
| File too large | Exceeds limit | Compress or split file |Rules for building apps on Blink infrastructure. Apply when using @blinkdotnew/sdk, blink CLI, or Blink MCP tools.
Rules for building apps on Blink infrastructure. Apply when using @blinkdotnew/sdk, blink CLI, or Blink MCP tools.
When building on Blink infrastructure:
- Initialize the SDK with both `projectId` and `publishableKey`:
```typescript
import { createClient } from '@blinkdotnew/sdk'
const blink = createClient({
projectId: import.meta.env.VITE_BLINK_PROJECT_ID || 'your-project-id',
publishableKey: import.meta.env.VITE_BLINK_PUBLISHABLE_KEY || 'blnk_pk_xxx',
auth: { mode: 'managed' },
})
```
- Use `blink.db.<tableName>` for database CRUD — never raw SQL from client code. The SDK auto-converts camelCase JS to snake_case SQL.
- Use `blink.auth` for authentication. Enable providers first: `blink auth-config set --provider google --enabled true`
- Backend must be a Hono server in `backend/index.ts` that exports default. Deploy with `blink backend deploy`.
- Build before deploying frontend: `npm run build && blink deploy ./dist --prod`
- Store secrets via `blink env set KEY value` — never hardcode API keys.
- SQLite booleans are integers: use `1`/`0`, not `true`/`false`. Filter with `{ isActive: 1 }`.
- All database IDs should be strings, generated with a prefix pattern like `usr_${generateId()}`.
- Queue tasks are delivered to `POST /api/queue` on the backend. Name tasks clearly: `send-welcome-email`, `process-image`.