# Using Embeddings

This page covers the runtime API for working with embeddings — the generated endpoints, the search service, and practical usage patterns.

## Embeddings API

When any feature has embeddings configured, the compiler generates these endpoints:

### Generate Embedding

```
POST /api/v1/embeddings
```

Enqueue an embedding job for arbitrary content:

```bash
curl -X POST https://api.example.com/api/v1/embeddings \
  -H "Content-Type: application/json" \
  -H "Cookie: better-auth.session_token=..." \
  -d '{
    "content": "Text to generate an embedding for",
    "model": "@cf/baai/bge-base-en-v1.5",
    "table": "jobs",
    "id": "job_123"
  }'
```

**Request body:**

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `content` | string | Yes | Text to embed |
| `model` | string | No | Embedding model (defaults to table config or `@cf/baai/bge-base-en-v1.5`) |
| `table` | string | No | Table to store the embedding in |
| `id` | string | Conditional | Record ID (required when `table` is provided) |

**Response:**

```json
{
  "queued": true,
  "jobId": "550e8400-e29b-41d4-a716-446655440000",
  "table": "jobs",
  "id": "job_123",
  "model": "@cf/baai/bge-base-en-v1.5"
}
```

This endpoint is useful for:
- Re-embedding existing records
- Embedding content that doesn't go through CRUD routes
- Batch embedding via scripts

### List Embedding Tables

```
GET /api/v1/embeddings/tables
```

List tables that have embeddings configured:

```json
{
  "tables": [
    {
      "name": "jobs",
      "embeddingColumn": "embedding",
      "model": "@cf/baai/bge-base-en-v1.5"
    }
  ]
}
```

## Search Service

For typed similarity search with classification, use the `createEmbeddings()` service generated from `defineEmbedding()` configurations.

### Basic Search

```typescript
const embeddings = createEmbeddings(ctx.env);

const results = await embeddings.jobSimilarity.search(
  "Senior backend engineer with distributed systems experience",
  {
    department: "Engineering",
    limit: 5,
    threshold: 0.70,
  }
);

// Returns: [{ id, score, classification, metadata }]
for (const match of results) {
  console.log(`${match.classification}: ${match.id} (score: ${match.score})`);
}
```

### Classification

Results are automatically classified based on similarity score. The four
classification names are fixed; only the cutoffs are configurable, via
`search.classify` on the `defineEmbedding()` config:

| Classification | Default Threshold | Meaning |
|----------------|-------------------|---------|
| `DUPLICATE` | >= 0.90 | Near-identical to an existing record |
| `CONFIRMS` | >= 0.85 | Strongly similar — corroborates the existing record |
| `RELATED` | >= 0.75 | Topically related |
| `NEW` | < 0.75 | No significant match |

`classify` accepts only the keys `DUPLICATE`, `CONFIRMS`, and `RELATED` (`NEW`
is the implicit floor). Any other key is silently ignored and its band keeps the
default cutoff — see [Classification Thresholds](/platform/vector/embeddings#classification-thresholds).

### Gray Zone Detection

For cases where automatic classification isn't sufficient, use gray zone detection:

```typescript
const results = await embeddings.jobSimilarity.findWithGrayZone(
  "Full-stack developer with React and Node.js",
  { min: 0.60, max: 0.85 }
);

// results.high_confidence — Score >= 0.85 (auto-classified)
// results.gray_zone — 0.60 <= score < 0.85 (needs review)
```

### Raw Embedding

Generate an embedding vector without searching:

```typescript
const vector = await embeddings.jobSimilarity.embed("Text to embed");
// Returns: number[] (768 dimensions for bge-base)
```

## Vectorize Queries

If your project uses a Vectorize index, you can query it directly for custom search logic:

```typescript
// Generate embedding for query text
const queryVector = await embeddings.jobSimilarity.embed(searchText);

// Query Vectorize with metadata filters
const results = await ctx.env.VECTORIZE.query(queryVector, {
  topK: 10,
  filter: {
    department: "Engineering",
    organizationId: ctx.activeOrgId,
  },
});
```

The `metadata` fields in your `defineTable` embeddings config are automatically included in the Vectorize index, enabling filtered searches.

## Auto-Embed vs Manual

| Trigger | How | Use Case |
|---------|-----|----------|
| Auto (INSERT) | Compiler enqueues after successful create | Default behavior |
| Auto (UPDATE) | Compiler enqueues when watched fields change | Keeps embeddings fresh |
| Manual (API) | `POST /api/v1/embeddings` | Re-embedding, batch jobs |
| Manual (Queue) | `env.EMBEDDINGS_QUEUE.send(...)` | Custom pipelines |

## Security

- The embeddings API requires authentication
- Jobs are scoped to the user's `activeOrgId`
- Embedding jobs are only enqueued after all security checks pass (auth, firewall, guards)
- The queue consumer is an internal process — it trusts pre-validated jobs

## Supported Models

Any Workers AI embedding model can be used:

| Model | Dimensions | Notes |
|-------|------------|-------|
| `@cf/baai/bge-base-en-v1.5` | 768 | Default, good general-purpose |
| `@cf/baai/bge-small-en-v1.5` | 384 | Faster, smaller |
| `@cf/baai/bge-large-en-v1.5` | 1024 | Higher quality |

See [Workers AI Models](https://developers.cloudflare.com/workers-ai/models/) for the full list.

## Cloudflare Only

Embeddings require Cloudflare Workers AI and Queues. Cloudflare is the only runtime provider Quickback ships (`defineRuntime("cloudflare")`), so this is not a portability constraint you have to design around.

## See Also

- [Automatic Embeddings](/platform/vector/embeddings) — `defineTable()` config, `defineEmbedding()` search service, and Vectorize integration
- [Queues](/platform/queues) — How embedding jobs are processed
