Using Embeddings
Re-embedding rows, semantic search, and practical usage patterns.
This page covers the runtime API for working with embeddings — re-embedding rows, the search service, and practical usage patterns.
Re-embedding Rows
Embeddings have no endpoint of their own. Rows are embedded on INSERT and
re-embedded on UPDATE by the generated CRUD routes, after auth, firewall, and
guards pass — so PATCH the row to re-embed (with onUpdate: [...fields],
include a watched field; resending its current value is enough). The job uses the
table's configured model and embeddingColumn.
Search Service
For typed similarity search with classification, use the createEmbeddings() service generated from defineEmbedding() configurations.
Basic Search
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.
Gray Zone Detection
For cases where automatic classification isn't sufficient, use gray zone detection:
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:
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:
// 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 |
| Re-embed | PATCH the row (or PATCH /batch) | Re-embedding existing records |
| Manual (Queue) | env.EMBEDDINGS_QUEUE.send(...) | Custom pipelines |
Security
- Embedding jobs are only enqueued by the generated write routes, after all security checks pass (auth, firewall, guards) — there is no client-facing embeddings endpoint
- Jobs are scoped to the writer's
activeOrgId - 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 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 —
defineTable()config,defineEmbedding()search service, and Vectorize integration - Queues — How embedding jobs are processed