Overview
The Formict Public API lets you integrate your existing systems — ERPs, CRMs, dispatch platforms, or custom workflows — with Formict. You can create and manage jobs, customers, vehicles, locations, and shifts programmatically.
Requirements
- Plan: Pro or Enterprise (API access is not available on Free or Starter plans)
- API Key: Generate one from Settings → API Keys in the Formict dashboard
Interactive playground
A live interactive API playground is available at api.formict.com/v1/public/docs. You can browse endpoints, fill in parameters, paste your API key, and send real requests directly from the browser.
Authentication
All API requests must include your API key in the Authorization header
using the Bearer scheme:
Authorization: Bearer fmct_live_xxxxxxxxxxxxxxxxxxxxxxxx API key format
API keys follow the format fmct_live_ followed by a random string.
The fmct_live_ prefix is the public identifier visible in the dashboard.
The full key is shown only once at creation — store it securely.
Scopes
Each API key is created with one or more scopes that determine what it can do:
| Scope | Permissions |
|---|---|
read | Read resources (GET requests) |
write | Create and update resources (POST, PATCH, DELETE requests) |
admin | Full access including team and settings |
A request will fail with 403 Forbidden if the API key lacks the required scope.
Security best practices
- Never expose API keys in client-side code or public repositories
- Use the minimum required scope for each integration
- Set an expiration date for keys that don't need permanent access
- Regenerate keys immediately if you suspect they've been compromised
- Use separate keys for different integrations to isolate access
Request Format
Base URL
https://api.formict.com/v1/public Content type
All request bodies must be JSON with the Content-Type header:
Content-Type: application/json Example request
curl -X GET "https://api.formict.com/v1/public/jobs?page=1&limit=20" \
-H "Authorization: Bearer fmct_live_xxxxxxxx" \
-H "Content-Type: application/json" Response Format
All responses are JSON. Successful responses wrap data in a data envelope:
{
"data": {
"jobs": [...],
"pagination": {
"page": 1,
"limit": 20,
"total": 42,
"totalPages": 3
}
},
"message": "Success"
} Single-resource responses follow the same pattern:
{
"data": {
"job": {
"id": "clx1234...",
"title": "AC Maintenance",
"status": "CREATED",
...
}
},
"message": "Success"
} HTTP status codes
| Status | Meaning |
|---|---|
200 | Success |
201 | Resource created |
400 | Bad request (validation error) |
401 | Unauthorized (missing or invalid API key) |
403 | Forbidden (insufficient scope) |
404 | Resource not found |
422 | Unprocessable entity (business logic error) |
429 | Rate limit exceeded |
500 | Internal server error |
Error Handling
Error responses include an error field with a human-readable message
and optionally a machine-readable code:
{
"error": "Job not found",
"code": "NOT_FOUND"
} Common error codes
| Code | Description |
|---|---|
NOT_FOUND | The requested resource does not exist or was deleted |
VALIDATION_ERROR | Request body failed validation (missing or invalid fields) |
TERMINAL_STATE | Cannot modify a job in COMPLETED or CANCELLED status |
JOB_IN_PROGRESS | Cannot delete a job that is currently in progress |
FORBIDDEN | API key lacks the required scope for this operation |
RATE_LIMITED | Too many requests — wait and retry |
Pagination
List endpoints support cursor-based pagination with page and limit
query parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
page | number | 1 | Page number (1-indexed) |
limit | number | 20 | Items per page (max 100) |
The response includes a pagination object:
"pagination": {
"page": 1,
"limit": 20,
"total": 42,
"totalPages": 3
} Rate Limits
API keys have configurable rate limits set at creation time (requests per minute).
When you exceed the rate limit, the API returns 429 Too Many Requests.
The response includes headers indicating your current rate limit status:
| Header | Description |
|---|---|
X-RateLimit-Limit | Maximum requests per minute |
X-RateLimit-Remaining | Requests remaining in current window |
X-RateLimit-Reset | Seconds until the rate limit resets |
Jobs
Jobs are field service work items — deliveries, maintenance calls, inspections, or any task performed at a customer's location.
GET /jobs
Returns a paginated list of jobs in your organization. Requires read scope.
Query parameters
| Parameter | Type | Description |
|---|---|---|
page | number | Page number (default: 1) |
limit | number | Items per page (default: 20, max: 100) |
status | string | Filter by status: CREATED, ASSIGNED, IN_PROGRESS, COMPLETED, CANCELLED |
customerId | string | Filter by customer ID |
locationId | string | Filter by location ID |
priority | string | Filter by priority: LOW, NORMAL, HIGH |
targetDate | string | Filter by date (YYYY-MM-DD) — returns jobs with time windows overlapping this date |
Example request
curl "https://api.formict.com/v1/public/jobs?status=CREATED&limit=10" \
-H "Authorization: Bearer fmct_live_xxxxxxxx" Example response
{
"data": {
"jobs": [
{
"id": "clx1abc...",
"title": "AC Maintenance",
"description": "Annual AC unit service",
"status": "CREATED",
"priority": "NORMAL",
"customerId": "clx2def...",
"locationId": "clx3ghi...",
"jobLatitude": "-6.2088",
"jobLongitude": "106.8456",
"serviceDuration": 3600,
"timeWindowType": "HARD",
"earliestStart": "2025-03-15T08:00:00.000Z",
"latestEnd": "2025-03-15T12:00:00.000Z",
"scheduledStart": null,
"scheduledEnd": null,
"assignedUserId": null,
"startedAt": null,
"completedAt": null,
"createdAt": "2025-03-14T10:00:00.000Z",
"updatedAt": "2025-03-14T10:00:00.000Z",
"customer": { "id": "clx2def...", "name": "PT Sejahtera" },
"assignedUser": null,
"location": { "id": "clx3ghi...", "name": "Jakarta Office" }
}
],
"pagination": {
"page": 1,
"limit": 10,
"total": 1,
"totalPages": 1
}
},
"message": "Success"
} GET /jobs/:id
Returns a single job with full details including customer, assigned worker, tags, and capacity demands. Requires read scope.
Path parameters
| Parameter | Type | Description |
|---|---|---|
id | string | Job ID |
Example response
{
"data": {
"job": {
"id": "clx1abc...",
"title": "AC Maintenance",
"description": "Annual AC unit service",
"status": "CREATED",
"priority": "NORMAL",
"customerId": "clx2def...",
"locationId": "clx3ghi...",
"jobLatitude": "-6.2088",
"jobLongitude": "106.8456",
"serviceDuration": 3600,
"timeWindowType": "HARD",
"earliestStart": "2025-03-15T08:00:00.000Z",
"latestEnd": "2025-03-15T12:00:00.000Z",
"scheduledStart": null,
"scheduledEnd": null,
"assignedUserId": null,
"shiftId": null,
"startedAt": null,
"completedAt": null,
"actualDuration": null,
"createdAt": "2025-03-14T10:00:00.000Z",
"updatedAt": "2025-03-14T10:00:00.000Z",
"customer": {
"id": "clx2def...",
"name": "PT Sejahtera",
"address": "Jl. Sudirman 100",
"latitude": "-6.2088",
"longitude": "106.8456"
},
"assignedUser": null,
"location": { "id": "clx3ghi...", "name": "Jakarta Office" },
"tags": [
{ "tag": { "id": "clx4jkl...", "name": "HVAC" } }
],
"demands": [
{
"capacityDimension": { "id": "clx5mno...", "key": "weight", "label": "Weight" },
"requiredValue": 50
}
]
}
},
"message": "Success"
} POST /jobs
Creates a new job. Requires write scope. Returns 201 Created.
Request body
| Field | Type | Required | Description |
|---|---|---|---|
title | string | Yes | Job title (min 1 character) |
customerId | string | No | Customer ID |
locationId | string | No | Location/depot ID |
description | string | No | Detailed description |
jobLatitude | number | No | Job site latitude |
jobLongitude | number | No | Job site longitude |
priority | string | No | LOW, NORMAL, or HIGH |
serviceDuration | number | No | Duration in seconds (default: 900) |
timeWindowType | string | No | HARD or SOFT (default: HARD) |
earliestStart | string | No | ISO 8601 datetime |
latestEnd | string | No | ISO 8601 datetime (must be after earliestStart) |
Example request
curl -X POST "https://api.formict.com/v1/public/jobs" \
-H "Authorization: Bearer fmct_live_xxxxxxxx" \
-H "Content-Type: application/json" \
-d '{
"title": "AC Maintenance",
"customerId": "clx2def...",
"description": "Annual AC unit service",
"priority": "NORMAL",
"serviceDuration": 3600,
"timeWindowType": "HARD",
"earliestStart": "2025-03-15T08:00:00.000Z",
"latestEnd": "2025-03-15T12:00:00.000Z"
}' Error cases
400— Missing requiredtitlefield400—earliestStartis afterlatestEnd400— Customer or location ID not found in your organization
PATCH /jobs/:id
Updates an existing job. Only include the fields you want to change. Requires write scope.
Path parameters
| Parameter | Type | Description |
|---|---|---|
id | string | Job ID |
Request body
Same fields as POST /jobs, all optional. Only include fields you want to change.
Example request
curl -X PATCH "https://api.formict.com/v1/public/jobs/clx1abc..." \
-H "Authorization: Bearer fmct_live_xxxxxxxx" \
-H "Content-Type: application/json" \
-d '{ "priority": "HIGH", "serviceDuration": 7200 }' Error cases
404— Job not found422— Cannot update a job inCOMPLETEDorCANCELLEDstatus
DELETE /jobs/:id
Soft-deletes a job. The job is marked as deleted but retained in the database for data retention. Requires write scope.
Path parameters
| Parameter | Type | Description |
|---|---|---|
id | string | Job ID |
Example request
curl -X DELETE "https://api.formict.com/v1/public/jobs/clx1abc..." \
-H "Authorization: Bearer fmct_live_xxxxxxxx" Error cases
404— Job not found422— Cannot delete a job that isIN_PROGRESS
Customers
Customers represent the people or businesses where field work takes place. Each customer has a service location with GPS coordinates used for route optimization.
GET /customers
Returns a paginated list of customers. Requires read scope.
Query parameters
| Parameter | Type | Description |
|---|---|---|
page | number | Page number (default: 1) |
limit | number | Items per page (default: 50, max: 100) |
search | string | Search by name or address (case-insensitive) |
Example response
{
"data": {
"customers": [
{
"id": "clx2def...",
"name": "PT Sejahtera",
"address": "Jl. Sudirman 100, Jakarta",
"latitude": "-6.2088",
"longitude": "106.8456",
"serviceDuration": 1800,
"priority": "NORMAL",
"timeWindowType": "HARD",
"operationalStart": "08:00",
"operationalEnd": "17:00",
"createdAt": "2025-01-10T08:00:00.000Z",
"updatedAt": "2025-01-10T08:00:00.000Z"
}
],
"pagination": { "page": 1, "limit": 50, "total": 1, "totalPages": 1 }
},
"message": "Success"
} GET /customers/:id
Returns a single customer with all fields. Requires read scope.
POST /customers
Creates a new customer. Requires write scope. Returns 201 Created.
Request body
| Field | Type | Required | Description |
|---|---|---|---|
name | string | Yes | Customer name (min 1 character) |
latitude | number | Yes | Service location latitude |
longitude | number | Yes | Service location longitude |
address | string | No | Street address |
serviceDuration | number | No | Default service time in seconds |
priority | string | No | LOW, NORMAL, or HIGH |
timeWindowType | string | No | HARD or SOFT |
operationalStart | string | No | Opening time in HH:MM format |
operationalEnd | string | No | Closing time in HH:MM format |
Example request
curl -X POST "https://api.formict.com/v1/public/customers" \
-H "Authorization: Bearer fmct_live_xxxxxxxx" \
-H "Content-Type: application/json" \
-d '{
"name": "PT Sejahtera",
"address": "Jl. Sudirman 100, Jakarta",
"latitude": -6.2088,
"longitude": 106.8456,
"serviceDuration": 1800,
"operationalStart": "08:00",
"operationalEnd": "17:00"
}' PATCH /customers/:id
Updates an existing customer. Only include the fields you want to change. Requires write scope.
Request body
Same fields as POST /customers, all optional.
Vehicles
Vehicles represent your fleet. Each vehicle is assigned to a location (depot) and has capacity constraints, cost parameters, and capability tags.
GET /vehicles
Returns a paginated list of vehicles with tags and capacity info. Requires read scope.
Query parameters
| Parameter | Type | Description |
|---|---|---|
page | number | Page number (default: 1) |
limit | number | Items per page (default: 50, max: 100) |
locationId | string | Filter by depot/location ID |
isActive | boolean | Filter by active status |
Example response
{
"data": {
"vehicles": [
{
"id": "clx4jkl...",
"name": "Van-01",
"locationId": "clx3ghi...",
"fixedCost": 50000,
"costPerKm": 500,
"costPerHour": 15000,
"speedFactor": 1.0,
"startLatitude": null,
"startLongitude": null,
"endPolicy": "RETURN_TO_ORIGIN",
"isActive": true,
"createdAt": "2025-01-10T08:00:00.000Z",
"updatedAt": "2025-01-10T08:00:00.000Z",
"location": { "id": "clx3ghi...", "name": "Jakarta Office" },
"tags": [
{ "tag": { "id": "clx5mno...", "name": "HVAC" } }
],
"capacities": [
{
"capacityDimension": { "id": "clx6pqr...", "key": "weight", "label": "Weight" },
"maxValue": 1000
}
]
}
],
"pagination": { "page": 1, "limit": 50, "total": 1, "totalPages": 1 }
},
"message": "Success"
} GET /vehicles/:id
Returns a single vehicle with tags and capacities. Requires read scope.
Locations
Locations are your operational hubs or depots. Vehicles start and end their routes at their assigned location.
GET /locations
Returns all locations in your organization. Requires read scope.
Query parameters
| Parameter | Type | Description |
|---|---|---|
page | number | Page number (default: 1) |
limit | number | Items per page (default: 50, max: 100) |
isActive | boolean | Filter by active status |
Example response
{
"data": {
"locations": [
{
"id": "clx3ghi...",
"name": "Jakarta Office",
"code": "JKT",
"description": "Main headquarters",
"address": "Jl. Sudirman 1, Jakarta",
"latitude": "-6.1751",
"longitude": "106.8272",
"timezone": "Asia/Jakarta",
"isPrimary": true,
"isActive": true,
"createdAt": "2025-01-01T00:00:00.000Z",
"updatedAt": "2025-01-01T00:00:00.000Z"
}
],
"pagination": { "page": 1, "limit": 50, "total": 1, "totalPages": 1 }
},
"message": "Success"
} GET /locations/:id
Returns a single location with all fields. Requires read scope.
Shifts
Shifts define when vehicles and workers are available. The route optimizer uses shift time windows as the available operating window for each vehicle.
GET /shifts
Returns a paginated list of shift templates with worker, location, vehicle, and break details. Requires read scope.
Query parameters
| Parameter | Type | Description |
|---|---|---|
page | number | Page number (default: 1) |
limit | number | Items per page (default: 50, max: 100) |
locationId | string | Filter by location ID |
userId | string | Filter by worker ID |
day | string | Filter by day of week (e.g., MONDAY) |
Example response
{
"data": {
"shifts": [
{
"id": "clx7stu...",
"userId": "clx8vwx...",
"locationId": "clx3ghi...",
"vehicleId": "clx4jkl...",
"day": "MONDAY",
"startTime": "08:00",
"endTime": "17:00",
"isGenerated": false,
"createdAt": "2025-01-10T08:00:00.000Z",
"updatedAt": "2025-01-10T08:00:00.000Z",
"user": { "id": "clx8vwx...", "name": "John", "email": "[email protected]" },
"location": { "id": "clx3ghi...", "name": "Jakarta Office" },
"vehicle": { "id": "clx4jkl...", "name": "Van-01" },
"breaks": [
{ "id": "clx9yza...", "startTime": "12:00", "endTime": "13:00", "isPaid": false }
]
}
],
"pagination": { "page": 1, "limit": 50, "total": 1, "totalPages": 1 }
},
"message": "Success"
} GET /shifts/:id
Returns a single shift with worker, location, vehicle, and break details. Requires read scope.
Enums & Constants
These are the valid values for enum fields used across the API.
JobStatus
| Value | Description |
|---|---|
CREATED | Job exists but is not assigned |
ASSIGNED | Job is assigned to a vehicle/worker |
IN_PROGRESS | Worker has started the job |
COMPLETED | Job is done |
CANCELLED | Job was cancelled |
JobPriority
| Value | Description |
|---|---|
LOW | Low priority |
NORMAL | Normal priority (default) |
HIGH | High priority — preferred by the optimizer |
TimeWindowType
| Value | Description |
|---|---|
HARD | Strict — job is dropped if it can't fit in the window |
SOFT | Flexible — solver penalizes but still assigns |
VehicleEndPolicy
| Value | Description |
|---|---|
RETURN_TO_ORIGIN | Vehicle returns to its route origin after last job |
END_AT_LAST_JOB | Route ends wherever the last job is |
Day of week
| Value |
|---|
MONDAY |
TUESDAY |
WEDNESDAY |
THURSDAY |
FRIDAY |
SATURDAY |
SUNDAY |
Duration format
All duration fields (serviceDuration, etc.) are in seconds.
For example, 30 minutes = 1800, 1 hour = 3600.
Datetime format
All datetime fields use ISO 8601 format in UTC:
2025-03-15T08:00:00.000Z
Coordinate format
Latitude and longitude are stored as strings in the database but accepted as numbers
in request bodies. Use decimal degrees (e.g., -6.2088 for latitude,
106.8456 for longitude).
Webhooks
Formict can send HTTP POST requests to your server when certain events occur. Webhooks are configured through Automations in the dashboard using the Send Webhook action type.
Setting up a webhook
- Go to Settings → Automations
- Create a new automation
- Select a trigger event (e.g.,
JOB_COMPLETED) - Choose Send Webhook as the action
- Enter your webhook URL
- Activate the automation
Webhook payload
The webhook sends a JSON POST request to your URL with the event data:
{
"event": "JOB_COMPLETED",
"timestamp": "2025-03-15T14:30:00.000Z",
"data": {
"jobId": "clx1abc...",
"title": "AC Maintenance",
"status": "COMPLETED",
"customerId": "clx2def...",
"assignedUserId": "clx8vwx...",
"completedAt": "2025-03-15T14:30:00.000Z"
}
} Available trigger events
| Event | Description |
|---|---|
JOB_CREATED | A new job is created |
JOB_UPDATED | A job's details are modified |
STATUS_CHANGED | A job's status transitions |
JOB_ASSIGNED | A job is assigned to a worker |
JOB_UNASSIGNED | A job is removed from a worker |
JOB_COMPLETED | A job is marked as completed |
JOB_CANCELLED | A job is cancelled |
FORM_SUBMITTED | A form submission is received |
Retry behavior
If your webhook endpoint returns a non-2xx status code, the request is not retried. Check the automation execution logs in the dashboard to see delivery status and any error details.
Changelog
v1.0 — Current
- Jobs CRUD (list, get, create, update, delete)
- Customers CRUD (list, get, create, update)
- Vehicles (list, get) with tags and capacities
- Locations (list, get)
- Shifts (list, get) with breaks
- API key authentication with scopes (read, write, admin)
- Webhook delivery via automations
Need help? Contact support or check the user documentation for step-by-step guides.