API endpoints
Optimize profile
auth:sanctum, tenant.ownership
Generate content
The enrichment flow
Request validation
The API controller validates the request and checks the tenant’s remaining AI budget. Suspended (
canceled, past_due) and archived (soft-deleted) tenants are rejected.Prompt construction
The Prompt templates live in
BusinessProfilePromptRequest DTO encapsulates the prompt content and optional model configuration:app/Domain/AIEnrichment/Prompts/.Job dispatch
The AI enrichment job is dispatched to the queue. It calls the configured AI provider, tracks token consumption, and writes the result back to the tenant profile.
Profile update
The job applies the AI-generated fields to the Fields that can be enriched:
Tenant model. Only non-empty values are applied:description, seo_title, seo_description, tags, slogan.Budget enforcement
AI budget is enforced at the application layer before any prompt is sent to the model provider. The budget system tracks credit/token consumption per tenant:- Each API call consumes a configurable number of credits.
- When the remaining budget reaches zero, the endpoint returns a
402 Payment Requiredor403 Forbiddenresponse depending on configuration. - Budget can be replenished by the super admin from
/admin. - The
is_ai_enhancedflag onTenantprevents re-enrichment by default unlessallowReEnhancement = trueis passed.
Audit log
All AI consumption is recorded via Spatie Activitylog under a dedicated log name. Each log entry includes:- Tenant ID
- User who triggered the request
- Fields that were modified
- Token count consumed (when the provider returns usage metadata)
- Timestamp
TenantProfileEnhanced domain event feeds into the outbox for reliable downstream processing (reporting, billing hooks).
Audit entries are visible in the super admin panel at /admin under Activity Logs, filtered by the ai-enrichment log name.
Domain invariants
| Condition | Result |
|---|---|
Subscription canceled or past_due | TenantDomainException::cannotEnhanceSuspendedProfile() |
| Tenant is soft-deleted | TenantDomainException::cannotEnhanceArchivedProfile() |
No owner assigned (user_id = null) | TenantDomainException::cannotEnhanceWithoutOwner() |
Already enhanced (is_ai_enhanced = true) | TenantDomainException::alreadyEnhanced() unless allowReEnhancement = true |