Inquiries API Documentation

Inquiry handling and workflow management

Inquiries API

Inquiry management and workflow automation system

Overview

The Inquiries API provides a webform submission endpoint for external platforms to send inquiries with personal contact data, business information, and inquiry details. The system automatically processes submissions, sends acknowledgment emails, and tracks inquiry status through its lifecycle.

Personal Data

  • Name and contact info
  • Address details
  • Phone and email

Business Data

  • Company details
  • Business address
  • Invoice & VAT info

Features

  • Type categorization with icons
  • Auto-acknowledgment
  • Per-type status pipelines
  • Status audit history
  • Transition validation
  • Offer management

Status engine: inquiry transitions are validated against the ordered, per-type pipeline from GET /api/inquiries/meta/statuses. Status history is written to the shared status package when available, with automatic fallback to the legacy inquiry history table.

Method Endpoint Description Requirements
Inquiry Types
GET /api/inquiries/meta/types Get all available inquiry types with icons and metadata. Each type includes a <code>statuses</code> array with the ordered workflow pipeline for that type.
  • include_inactive (optional) – Include inactive types (true/false, default: false)
Inquiry Submission
POST /api/inquiries Submit a new inquiry. User profile data (name, email, address) is fetched from UserHub using user_id — do not send personal data in this payload.
  • inquiry_type_id (optional) – Inquiry type ID from /api/inquiries/meta/types
  • user_id (required) – UserHub user ID of the inquiry owner (the customer). Always honoured from the payload; never overridden by the token holder.
  • created_by (optional) – UserHub user ID of the actor submitting this request. Omit when the customer submits their own inquiry; supply the contractor/admin ID when acting on behalf of a customer. If omitted the server derives it automatically from the proxy token context.
  • subject (required) – Inquiry subject
  • message (required) – Inquiry message
  • category (optional) – Category (e.g., construction, renovation)
  • budget (optional) – Budget amount in euros (decimal)
  • priority (optional) – Priority: low, normal, high, urgent (default: normal)
  • source (optional) – Source platform identifier (e.g., mijn-portal)
  • filter_option_ids (optional) – Array of filter option IDs
Inquiry Management
GET /api/inquiries/list List inquiries with filtering
  • inquiry_type (optional) – Filter by inquiry type ID or slug
  • status (optional) – Filter by status: open, in_progress, closed, cancelled
  • category (optional) – Filter by category
  • user_id (optional) – Filter by owning customer (returns all inquiries where user_id matches, including those created by a contractor on their behalf)
  • open_only (optional) – Show only open inquiries (true/false)
  • unassigned_only (optional) – Exclude inquiries already linked to a case (default: true)
  • include_linked_to_case (optional) – Override unassigned_only and include case-linked inquiries (true/false)
  • per_page (optional) – Results per page (default: 15)
GET /api/inquiries/{id} Retrieve inquiry details with related offers
  • id (required) – Inquiry ID
POST /api/inquiries/{id}/action Perform action on inquiry (update status, close)
  • id (required) – Inquiry ID
  • action (required) – Action name: update_status, close
  • + Additional parameters based on action
File Attachments
GET /api/inquiries/{id}/attachments List all file attachments for an inquiry
  • id (required) – Inquiry ID
  • user_id (required) – Requesting user ID (for authorization)
POST /api/inquiries/{id}/attachments Upload a file attachment to an inquiry (multipart/form-data)
  • id (required) – Inquiry ID
  • file (required) – File to upload (PDF, JPEG, PNG, GIF, WebP, DOC, DOCX, XLS, XLSX, TXT; max 10 MB)
  • user_id (required) – Uploading user ID (for authorization)
  • uploaded_by (optional) – Defaults to user_id if omitted
GET /api/inquiries/{id}/attachments/{attachmentId}/download Download an attachment file
  • id (required) – Inquiry ID
  • attachmentId (required) – Attachment ID
  • user_id (required) – Requesting user ID (for authorization)
DELETE /api/inquiries/{id}/attachments/{attachmentId} Delete an attachment (removes file from disk)
  • id (required) – Inquiry ID
  • attachmentId (required) – Attachment ID
  • user_id (required) – Requesting user ID (for authorization)
Status & History
GET /api/inquiries/meta/statuses Get all inquiry type status pipelines grouped by type slug. Each pipeline lists the ordered workflow steps with key, label, description, color, and terminal/initial flags.
  • type (optional) – Limit to a single type slug (e.g. quote-request)
GET /api/inquiries/{id}/history Get the full status change audit trail for an inquiry, ordered oldest first
  • id (required) – Inquiry ID

Request Examples

Get Available Inquiry Types

GET /api/inquiries/meta/types

Response:

{ "success": true, "data": [ { "id": 1, "name": "Question", "slug": "question", "icon": "❓", "icon_type": "emoji", "icon_display": "❓", "color": "#3B82F6", "description": "General questions or inquiries about services or products", "is_active": true, "sort_order": 1, "statuses": [ { "key": "submitted", "label": "Submitted", "color": "#6B7280", "is_initial": true, "is_terminal": false }, { "key": "in_review", "label": "In Review", "color": "#3B82F6", "is_initial": false, "is_terminal": false }, { "key": "answered", "label": "Answered", "color": "#10B981", "is_initial": false, "is_terminal": false }, { "key": "closed", "label": "Closed", "color": "#059669", "is_initial": false, "is_terminal": true } ] }, { "id": 2, "name": "Call-back Request", "slug": "callback_request", "icon": "📞", "icon_type": "emoji", "icon_display": "📞", "color": "#10B981", "description": "Request for a phone call from our team", "is_active": true, "sort_order": 2, "statuses": ["... pipeline steps ..."] } ] }

Submit Inquiry (customer submits their own request)

POST /api/inquiries

User profile data (name, email, address) is fetched from UserHub using user_id. Do not send personal data in this payload.

{ "inquiry_type_id": 1, "user_id": 123, "subject": "Verbouwing keuken", "message": "Ik wil graag mijn keuken laten verbouwen en zou graag een offerte ontvangen.", "category": "renovation", "budget": 4500.00, "priority": "normal", "source": "mijn-portal" }

Submit Inquiry (contractor/admin on behalf of a customer)

POST /api/inquiries

Pass the customer's ID as user_id (ownership & visibility) and the contractor's/admin's ID as created_by (audit trail). The server honours user_id from the payload without overriding it with the token holder's identity. If created_by is omitted the server derives it automatically from the proxy token context.

{ "inquiry_type_id": 3, "user_id": 456, "created_by": 789, "subject": "Aanvraag namens klant", "message": "De klant wil graag informatie over de mogelijkheden voor aanbouw.", "category": "construction", "priority": "normal", "source": "mijn-portal" }

Success Response (201 Created)

user_id is the owning customer. created_by is the actor who submitted the request (same as user_id when the customer submits their own inquiry; different when a contractor/admin acts on their behalf).

{ "success": true, "message": "Inquiry submitted successfully", "data": { "inquiry_id": 42, "reference": 42, "status": "open", "user_id": 456, "created_by": 789, "created_at": "2026-04-16T10:30:00+00:00" } }

Get Inquiry Details

GET /api/inquiries/42

{ "success": true, "data": { "inquiry": { "id": 42, "inquiry_type_id": 1, "inquiry_type": { "id": 1, "name": "Question", "slug": "question", "icon": "❓", "color": "#3B82F6" }, "first_name": "Jan", "last_name": "de Vries", "email": "jan.devries@example.com", "subject": "Verbouwing keuken", "status": "in_progress", "priority": "normal", "category": "renovation", "budget": 450.00, "budget_display": "€ 450,00", "full_name": "Jan de Vries", "truncated_message": "Ik wil graag mijn keuken laten verbouwen en zou graag...", "created_at": "2025-11-06T10:30:00.000000Z", "responded_at": "2025-11-06T14:15:00.000000Z" }, "offers_count": 3 } }

List Inquiries with Filtering

GET /api/inquiries/list?status=open&per_page=10

{ "success": true, "data": [ { "id": 42, "inquiry_type_id": 1, "inquiry_type": { "id": 1, "name": "Question", "slug": "question", "icon": "❓", "color": "#3B82F6" }, "first_name": "Jan", "last_name": "de Vries", "subject": "Verbouwing keuken", "status": "open", "budget": 450.00, "budget_display": "€ 450,00", "full_name": "Jan de Vries", "truncated_message": "Ik wil graag mijn keuken laten verbouwen en zou graag...", "created_at": "2025-11-06T10:30:00.000000Z" } ], "pagination": { "current_page": 1, "per_page": 10, "total": 127, "last_page": 13 } }

Filter by Inquiry Type

GET /api/inquiries/list?inquiry_type=callback_request

You can filter by type slug or ID: ?inquiry_type=2

{ "success": true, "data": [ { "id": 45, "inquiry_type_id": 2, "inquiry_type": { "id": 2, "name": "Call-back Request", "slug": "callback_request", "icon": "📞", "color": "#10B981" }, "first_name": "Maria", "last_name": "van Dam", "phone": "+31687654321", "subject": "Graag telefonisch contact", "status": "open", "priority": "high", "created_at": "2025-11-06T11:45:00.000000Z" } ], "pagination": { "current_page": 1, "per_page": 15, "total": 23, "last_page": 2 } }

Get Inquiry Type Status Pipelines

GET /api/inquiries/meta/statuses

Returns all workflow pipelines grouped by type slug. Use ?type=offer_request to limit to one type. The maps_to_case_status field indicates the advisory case status a transitioning inquiry maps to (if any).

{ "success": true, "data": { "question": [ { "key": "submitted", "label": "Submitted", "description": "Inquiry received and queued for review.", "color": "#6B7280", "sort_order": 1, "is_initial": true, "is_terminal": false, "maps_to_case_status": "open" }, { "key": "in_review", "label": "In Review", "description": "A team member is reviewing the inquiry.", "color": "#3B82F6", "sort_order": 2, "is_initial": false, "is_terminal": false, "maps_to_case_status": "in_progress" }, { "key": "answered", "label": "Answered", "description": "A response has been sent to the customer.", "color": "#10B981", "sort_order": 3, "is_initial": false, "is_terminal": false, "maps_to_case_status": "resolved" }, { "key": "closed", "label": "Closed", "description": "Inquiry fully handled and archived.", "color": "#059669", "sort_order": 4, "is_initial": false, "is_terminal": true, "maps_to_case_status": "closed" } ], "offer_request": [ { "key": "submitted", "label": "Submitted", "sort_order": 1, "is_initial": true, "is_terminal": false }, { "key": "offer_created", "label": "Offer Created", "sort_order": 2, "is_initial": false, "is_terminal": false }, { "...": "14 steps total for offer_request pipeline" } ] } }

Transition Validation Error Example

POST /api/inquiries/42/action

When a requested transition is not part of the inquiry type pipeline, the API rejects it.

{ "action": "update_status", "status": "closed" } // Response (500 with domain error message) { "success": false, "message": "Failed to perform action", "error": "Invalid inquiry status transition from submitted to closed" }

Get Inquiry Status History

GET /api/inquiries/42/history

Returns the append-only audit trail of all status transitions for the inquiry, ordered oldest first. from_status is null for the initial creation entry. changed_by is the UserHub user ID of the actor who made the change (may be null for system-triggered changes).

{ "success": true, "data": [ { "from_status": null, "to_status": "submitted", "changed_by": 789, "note": "Inquiry created", "created_at": "2026-04-16T10:30:00+00:00" }, { "from_status": "submitted", "to_status": "in_review", "changed_by": null, "note": null, "created_at": "2026-04-16T14:05:00+00:00" }, { "from_status": "in_review", "to_status": "answered", "changed_by": null, "note": "Response sent via email", "created_at": "2026-04-17T09:20:00+00:00" } ] }