Data model — AiLocationImportBatch
Header record for an AI-driven location import session; pairs with AiLocationImportItem rows that hold the individual generated drafts the user reviews before importing.
See Backend — AI services for the full import flow, job architecture, and Google Gemini call details.
Table: ai_location_import_batches
| Column | Type | Nullable | Default | Description |
|---|---|---|---|---|
id | bigint (PK) | No | — | Auto-increment |
workspace_id | bigint (FK → workspaces) | No | — | Owning workspace; cascade delete |
finder_id | bigint (FK → finders) | Yes | null | Scoped to a specific finder; nulled on finder delete (added in migration 2026_04_21) |
user_id | bigint (FK → users) | No | — | User who initiated the batch; cascade delete |
status | varchar | No | draft | State machine value — see Status enum below |
source_answers | json | No | — | Guided-questionnaire answers posted by the user (brand, business type, location count, geography, etc.) used to build the Gemini prompt |
model_name | varchar | Yes | null | Gemini model used to generate this batch (e.g. gemini-2.5-flash-lite) — stored for reproducibility |
prompt_version | varchar | No | v1 | Prompt template version — current value is v4 |
usage_metadata | json | Yes | null | Token-count data returned by Gemini — see usage_metadata shape |
total_items | int (unsigned) | No | 0 | Total number of draft items in this batch |
approved_items | int (unsigned) | No | 0 | Items the user explicitly approved |
rejected_items | int (unsigned) | No | 0 | Items the user explicitly rejected |
imported_items | int (unsigned) | No | 0 | Items successfully written to locations |
failed_items | int (unsigned) | No | 0 | Items that failed during the import step |
created_at | timestamp | No | — | Laravel standard |
updated_at | timestamp | No | — | Laravel standard |
Indexes: (workspace_id, status), (user_id, status)
Table: ai_location_import_items
One row per generated location draft. Belongs to a batch via batch_id.
| Column | Type | Nullable | Default | Description |
|---|---|---|---|---|
id | bigint (PK) | No | — | Auto-increment |
batch_id | bigint (FK → ai_location_import_batches) | No | — | Parent batch; cascade delete |
status | varchar | No | pending_review | Item state machine value — see Item status enum |
raw_model_output | json | Yes | null | Raw object for this location as returned by Gemini before normalization |
normalized_payload | json | No | — | Normalized location data after post-processing and Google Places enrichment |
user_payload | json | Yes | null | Edits the user made to normalized_payload before approving |
warnings | json | Yes | null | Array of non-blocking warning strings (e.g. “phone format unrecognized”) |
validation_errors | json | Yes | null | Array of validation errors that prevent import |
confidence | varchar | Yes | null | Confidence score: high, medium, or low — see scoring rules below |
import_result | json | Yes | null | Result written after the import step (e.g. {location_id: 42} on success, error detail on failure) |
created_at | timestamp | No | — | — |
updated_at | timestamp | No | — | — |
Indexes: (batch_id, status)
Relations
AiLocationImportBatch::workspace()—belongsTo(Workspace::class)AiLocationImportBatch::user()—belongsTo(User::class)AiLocationImportBatch::items()—hasMany(AiLocationImportItem::class, 'batch_id')->orderBy('id')AiLocationImportItem::batch()—belongsTo(AiLocationImportBatch::class, 'batch_id')
Status enum
Batch statuses
| Value | Meaning |
|---|---|
draft | Default; batch has items pending review or approval |
imported | All items have been imported (none remain pending or approved) |
discarded | Batch was deleted by the user (DELETE /ai-location-imports/{id}) |
Item status enum
| Value | Meaning |
|---|---|
pending_review | Default; awaiting user decision |
approved | User approved the draft |
rejected | User rejected the draft |
imported | Successfully written to locations |
failed | Import step wrote to this item but the location create failed |
JSON columns
source_answers
The questionnaire payload from the user — keys vary but typically include fields like business type, location count, geography, language, and optional brand context. These are embedded verbatim into the Gemini prompt by buildPrompt().
usage_metadata
Token usage extracted from Gemini’s usageMetadata response object. Shape:
{
"promptTokenCount": 1234,
"candidatesTokenCount": 567,
"totalTokenCount": 1801,
"thoughtsTokenCount": 0,
"toolUsePromptTokenCount": 0,
"raw": { ... }
}The original Gemini usageMetadata object is preserved under raw. Cost can be estimated using the GEMINI_2_5_FLASH_LITE_INPUT_PER_MILLION / OUTPUT_PER_MILLION env vars.
normalized_payload and user_payload (items)
normalized_payload is the output of the Google Places enrichment step applied to Gemini’s raw output. Fields: name, address, city, state, zip, country, phone, website, lat, lng, tags, categories, custom_fields, and optional hours.
user_payload overlays the user’s edits onto normalized_payload — the import step merges both and uses user_payload values where present.
import_result (items)
On success: {"location_id": 42}. On failure: an object with the error detail. Null until the apply step runs.
Confidence scoring
Computed per item by AiLocationImportController:
| Score | Condition |
|---|---|
low | Any validation_errors present |
high | ≥ 6 core fields filled and ≤ 1 warning |
medium | All other cases |
Related Process record
The generate step creates a Process record of type ai_location_import_generate; the apply step creates one of type ai_location_import_apply. The Process.related_type / related_id columns point back to this batch. See Data model — Process for the state machine.
Route surface (for reference)
| Method | Path | Notes |
|---|---|---|
GET | /api/ai-location-imports | List batches |
POST | /api/ai-location-imports | Create batch + dispatch generate job |
GET | /api/ai-location-imports/{id} | Single batch with items |
PATCH | /api/ai-location-imports/{id}/items/{itemId} | Approve/reject/edit a draft item |
POST | /api/ai-location-imports/{id}/import | Auto-approve valid pending items, run apply job synchronously |
DELETE | /api/ai-location-imports/{id} | Set status to discarded |