MiVida ↔ In4Suite Integration API
Everything the In4Suite team needs to push leads and opportunities into MiVida, keep them up to date, and receive changes back.
01Terminology
The two systems use the same words for different things. In this guide, “lead” and “opportunity” always mean In4Suite’s terms unless stated otherwise.
02Quick start
Three calls: get a token, push the customer, push their opportunity.
# 1. Get an access token (valid 24 hours) curl -X POST https://<mivida-host>/api/auth/token \ -H 'Content-Type: application/json' \ -d '{"client_id": "<your client id>", "client_secret": "<your client secret>"}' # → {"token": "eyJ0eXAiOiJKV1Qi..."} # 2. Push a lead (customer profile) curl -X POST https://<mivida-host>/api/in4suite/leads \ -H 'Authorization: Bearer eyJ0eXAiOiJKV1Qi...' \ -H 'Content-Type: application/json' \ -d '{ "lead_id": "LD-000123", "customer_name": "Aisha Rahman", "customer_phone": "+971501234567", "customer_country_code": "AE", "customer_email": "aisha.rahman@example.com", "created_at": "2024-03-14T10:22:00+04:00" }' # 3. Push an opportunity for that lead curl -X POST https://<mivida-host>/api/in4suite/opportunities \ -H 'Authorization: Bearer eyJ0eXAiOiJKV1Qi...' \ -H 'Content-Type: application/json' \ -d '{ "opportunity_id": "OP-004567", "lead_id": "LD-000123", "customer_name": "Aisha Rahman", "customer_phone": "+971501234567", "customer_country_code": "AE", "project_id": "PRJ-104", "area": "0192a1b2-55aa-7b31-8c12-44ab77cd1200", "unit_type": "2 BHK Type B", "budget": "1500000-1800000", "source_category": "Digital", "source": "Facebook", "assigned_agent_id": "USR-0042", "created_at": "2024-03-14T10:25:00+04:00" }'
03Authentication
MiVida uses the OAuth2 client-credentials grant. MiVida issues you a client_id and client_secret through a secure channel, never in plain-text email.
| Step | Request |
|---|---|
| Get a token | POST /api/auth/token with {"client_id": "...", "client_secret": "..."} |
| Use it | Header Authorization: Bearer <token> on every /api/in4suite/* call |
- Tokens are valid for 24 hours. Request a new one before expiry, or when a call returns 401.
- The
/api/in4suite/*endpoints accept only tokens issued to the In4Suite client. Any other token, including a MiVida user’s login token, gets 403. - Invalid credentials on
/auth/tokenreturn 403Invalid client credentials.
04Conventions
Your IDs are the keys
Every record is addressed by your ID (lead_id, opportunity_id). You never need to store MiVida’s IDs, though responses include them (mivida_customer_id, mivida_opportunity_id, mivida_lead_id, mivida_project_id) for support conversations. IDs are strings of up to 64 characters and may contain anything except /.
Projects and agents are addressed by your IDs too: MiVida stores your project id and agent id on its own project and employee records (fields, agent assignment).
Create-or-update with POST is safe to retry
POST creates the record if MiVida doesn’t know the ID yet, and updates it if it does. Re-sending the same payload never creates duplicates, so retrying after a timeout or network error is always safe.
| result | HTTP | Meaning |
|---|---|---|
created | 201 | New record created. |
updated | 200 | Existing record updated. |
linked | 200 | Leads only. MiVida already had this customer (same email, else same phone) and linked it to your lead_id instead of creating a duplicate. |
PUT and PATCH on /…/{id} only update. They return 404 if MiVida doesn’t know the ID.
Partial updates
On update, only the fields you send change. Fields you omit keep their current value.
- “Required” fields are required to create. On update you may omit them, but you can’t blank them:
""ornullgives 422. created_atis used on create only and ignored on update.- Unknown fields are ignored, so extra fields won’t break a call.
Dates and times
- Send ISO 8601 with a UTC offset, e.g.
2024-03-14T10:22:00+04:00or2024-03-14T06:22:00Z. A value without an offset is read in MiVida’s business timezone. - Received timestamps are always UTC, e.g.
2024-03-14T06:22:00Z. created_atmay not be in the future. Five minutes of clock skew are tolerated.
Response shapes
Single write (POST, PUT, PATCH). warnings lists non-fatal notes. The record was saved (example).
{ "result": "created", "warnings": [], "data": { ...record... } }Lookup (GET):
{ "data": { ...record... } }Validation failure (HTTP 422). Every problem is listed, by field name:
{
"errors": [
{ "field": "customer_country_code", "message": "customer_country_code \"ZZ\" is not a known ISO2 country code or dial code." },
{ "field": "created_at", "message": "created_at cannot be in the future." }
],
"code": 422
}Other errors (400, 401, 403, 404, 500):
{ "errors": { "name": "Not Found", "message": "No record with lead_id \"LD-9\" exists in MiVida.", "status": 404 }, "code": 404 }HTTP status summary
| Status | When | What to do |
|---|---|---|
| 200 / 201 | Success | — |
| 400 | Body isn’t valid JSON or not the expected shape, or a batch has more than 100 items | Fix the request |
| 401 | Missing, invalid or expired token | Get a new token and retry |
| 403 | Token isn’t the In4Suite client’s | Use the In4Suite credentials |
| 404 | GET, PUT or PATCH for an ID MiVida doesn’t know | Create it with POST |
| 422 | Validation failed | Fix the listed fields; don’t retry unchanged |
| 5xx | MiVida-side failure | Retry with backoff; contact MiVida if it persists |
05Endpoints
Base URL: https://<mivida-host>/api
| Method | Path | Purpose |
|---|---|---|
| POST | /in4suite/leads | Create or update a lead |
| PUT PATCH | /in4suite/leads/{lead_id} | Update a lead |
| GET | /in4suite/leads/{lead_id} | Fetch a lead as MiVida holds it |
| POST | /in4suite/leads/batch | Create or update up to 100 leads |
| POST | /in4suite/opportunities | Create or update an opportunity |
| PUT PATCH | /in4suite/opportunities/{opportunity_id} | Update an opportunity |
| GET | /in4suite/opportunities/{opportunity_id} | Fetch an opportunity, with MiVida’s lead status and current agent |
| POST | /in4suite/opportunities/batch | Create or update up to 100 opportunities |
06Leads (customer profiles)
Fields
| Field | To create | Type / limit | Notes |
|---|---|---|---|
lead_id | required | string ≤ 64 | Your Lead ID. |
customer_name | required | string ≤ 128 | |
customer_phone | required | string ≤ 15 | Unique across MiVida customers. Spaces, dashes, dots and brackets are removed; a leading + is kept. |
customer_country_code | required | string | ISO 3166-1 alpha-2 (AE) is preferred. Dial codes (971, +971, 00971) are accepted too. Shared dial codes resolve to the main country (1 → US, 7 → RU, 44 → GB, 61 → AU), so send ISO2 for the others. |
customer_email | required | email ≤ 128 | Unique across MiVida customers. |
created_at | required | ISO 8601 | Original creation time in In4Suite. Kept as the customer’s creation date. |
salutation | optional | string | master data your In4Suite salutation, e.g. Mr., M/s., Bishop. Matched ignoring case, spaces and dots (Dr. = Dr); M/s. and Ms. are distinct. One MiVida doesn’t know is stored as sent, not rejected. |
customer_category | optional | string ≤ 128 | master data customer category |
customer_location | optional | string ≤ 255 | Free text |
preferred_contact_method | optional | string ≤ 128 | master data contact method |
preferred_contact_time | optional | string ≤ 128 | master data contact time |
Behaviour
- Matching existing customers. If MiVida doesn’t know your
lead_idyet but already has a customer with the same email (checked first) or phone, that customer is linked to yourlead_id(result: "linked") and updated with your fields. No duplicate is created. - Conflict. If that email or phone belongs to a customer already linked to a different
lead_id, you get 422 naming the other lead. Two In4Suite leads can’t share an email or phone in MiVida. - Copies stay in sync. MiVida keeps a copy of the customer’s name, phone, email and country on each of their opportunities and leads. Updating the lead updates those copies too.
Example: update one field
PATCH /api/in4suite/leads/LD-000123
Content-Type: application/json
{ "customer_phone": "+971509999999" }{
"result": "updated",
"warnings": [],
"data": {
"lead_id": "LD-000123",
"mivida_customer_id": "0192a1b2-7c44-7e9a-bf21-2b1c9d4e5f60",
"customer_name": "Aisha Rahman",
"customer_phone": "+971509999999",
"customer_country_code": "AE",
"customer_email": "aisha.rahman@example.com",
"salutation": null,
"customer_category": null,
"customer_location": null,
"preferred_contact_method": null,
"preferred_contact_time": null,
"created_at": "2024-03-14T06:22:00Z",
"updated_at": "2026-10-07T08:15:31Z"
}
}07Opportunities
Fields
Fields marked master data are sent as your In4Suite names (e.g. 2 BHK Type B, Walk-In). MiVida keeps its lists in sync with In4Suite’s and matches names ignoring case, spaces and separators. area is the exception: a MiVida unit size id from the MiVida Master Data sheet. The customer’s email isn’t sent with the opportunity; MiVida takes it from the lead.
| Field | To create | Type / limit | Notes |
|---|---|---|---|
opportunity_id | required | string ≤ 64 | Your Opportunity ID. |
lead_id | required | string ≤ 64 | The customer’s lead_id. Push that lead first. Can’t be changed after the opportunity is created. |
customer_name | required | string ≤ 128 | |
customer_phone | required | string ≤ 15 | |
customer_country_code | required | string | Same rules as for leads. |
project_id | required | string | Your In4Suite project id (e.g. PRJ-104), matched against the In4Suite Project ID set on each MiVida project. MiVida project codes and ids are not accepted. |
source_category | required | string | master data your In4Suite source category, e.g. Walk-In. MiVida’s value (walk_in) is accepted too. |
unit_type | required | string | master data your In4Suite unit type, e.g. 2 BHK Type B. 2BHK, 2 BHK and 2_bhk are the same type. MiVida’s value (2_bhk_type_b) is accepted too. |
created_at | required | ISO 8601 | Original creation time in In4Suite. Decides whether the record is historical. |
area | optional | string (id) | master data MiVida unit size id. Must belong to project_id. MiVida fills in the phase, block and unit type from it. |
source | optional | string | master data your In4Suite source, e.g. Signages. Must belong to source_category. The same name may exist under several categories; the one under source_category is used. |
budget | optional | string ≤ 40 | One amount (1500000) or a range (1500000-1800000). |
assigned_agent_id | optional | string | Your In4Suite id of the agent. See agent assignment. |
customer_category | optional | string | master data |
customer_location | optional | string ≤ 255 | Free text |
buying_horizon | optional | string | master data |
preferred_contact_method | optional | string | master data |
preferred_contact_time | optional | string | master data |
remarks | optional | string ≤ 255 | Note for the assigning manager |
Agent assignment
Agents are identified by your In4Suite id for them, matched exactly against the In4Suite ID set on each MiVida employee. Employee codes are not accepted.
| Situation | Outcome |
|---|---|
| Id matches an employee with a MiVida user account | Assigned. MiVida creates its lead for that agent. |
| Id matches an employee without a user account | Saved unassigned, with your id kept. warnings says so. Status 201/200, not an error. A MiVida admin can assign it once the account exists. |
| Id not mapped to any MiVida employee | 422 on assigned_agent_id |
| Not sent, live record | MiVida assigns automatically, round-robin among the project cluster’s agents |
| Not sent, historical record | Left unassigned |
| Update with a different id | MiVida’s lead is reassigned: recorded in its history, and the new agent is notified (not for historical records). |
| Update with an empty value | Ignored. Agents can’t be removed through the API. |
{
"result": "created",
"warnings": ["Employee USR-0222 has no MiVida user account; the opportunity is left unassigned. It can be assigned once the account exists."],
"data": { "opportunity_id": "OP-004568", "status": "unassigned", "is_agent_assigned": false, "assigned_agent_id": "USR-0222", "...": "..." }
}Response fields
| Field | Meaning |
|---|---|
opportunity_id, lead_id | Your IDs |
mivida_opportunity_id | MiVida’s opportunity id |
mivida_lead_id | MiVida’s lead id once an agent is assigned; null before |
status | unassigned or assigned |
lead_status | Stage in MiVida’s sales pipeline (e.g. opportunity, site_visited, booked, sold); null while unassigned |
is_historical | Whether MiVida archived it on arrival |
assigned_agent_id | Your In4Suite id of the current agent. If that agent has no MiVida account, or MiVida reassigned it to someone not mapped to In4Suite, the last id you sent (null if you never sent one). |
is_agent_assigned | Whether a MiVida agent actually holds it |
project_id, mivida_project_id, project_code, area | Your In4Suite project id (as you send it), MiVida’s project id, MiVida’s project code and the unit size id |
| other fields | As sent, after normalization |
Rules to know
- One customer, many opportunities. A lead may have any number of opportunities. MiVida doesn’t treat them as duplicates.
- Changing
project_idwithoutareais rejected if the stored area belongs to the old project. Send the new project’sareawith it. - Changing
source_categorywithoutsourceis rejected if the stored source belongs to the old category. Send a source from the new category, or"source": ""to clear it. - A lead deleted in MiVida. If a MiVida user deletes the lead, In4Suite updates still apply to the opportunity, and the response carries a warning saying so.
- An opportunity deleted in MiVida can’t be re-created with the same
opportunity_id(422). Contact MiVida if this happens.
08Historical records
An opportunity whose created_at falls before today, in MiVida’s business timezone, is historical. MiVida decides this once, when the record is created; updates never change it.
Historical records:
- keep their original dates (created, assigned, stage), so reports place them in the right period;
- are archived: kept out of agents’ work lists, dashboards, funnel reports, automatic lead scoring and reminders;
- trigger no notifications to agents or customers, and are not synced back to In4Suite;
- are never auto-assigned;
- stay searchable in MiVida (filter “Historical: Yes”). A MiVida admin can un-archive one, for example when the customer comes back.
An opportunity created yesterday but delivered today, say after an outage, counts as historical. Deliver live opportunities on the day they’re created. A MiVida admin can un-archive any that arrive late.
09Batches and the initial load
Batch endpoints
POST /api/in4suite/opportunities/batch
Content-Type: application/json
{ "items": [ { ...opportunity... }, { ...opportunity... } ] }- Up to 100 items per request. More returns 400.
- Each item has the same fields as the single endpoint and is created or updated independently. A failing item doesn’t affect the others, even when MiVida hits an internal error on it: that item is reported as
failedwith an “internal error” message and the rest of the batch still runs. - The response is 200 whenever the body is well formed. Check each item’s
result.indexis the item’s position in youritemsarray.
{
"summary": { "total": 2, "created": 0, "updated": 1, "linked": 0, "failed": 1 },
"results": [
{ "index": 0, "opportunity_id": "OP-1", "result": "updated", "warnings": [],
"mivida_opportunity_id": "...", "mivida_lead_id": "...", "is_historical": true },
{ "index": 1, "opportunity_id": "OP-3", "result": "failed",
"errors": [ { "field": "lead_id", "message": "In4Suite lead \"LD-404\" has not been pushed to MiVida yet. Push the lead first, then retry this opportunity." } ] }
]
}Loading about 200,000 historical records
- All leads first, through
POST /in4suite/leads/batch. An opportunity is rejected if its lead hasn’t arrived yet. - Then all opportunities, through
POST /in4suite/opportunities/batch. - Send 100 items per request, with at most 4 requests in parallel.
- Collect the
faileditems, fix them and re-send only those. Re-sending items that already succeeded is harmless. - A failure reading “This record was being written by another request at the same time” means two parallel requests raced for the same ID. Retry it.
- Start with a dry run on MiVida’s staging environment using a few thousand records, to check the master-data mapping and throughput.
After the initial load, push changes as they happen through the single endpoints or batches.
10Changes sent back to In4Suite
MiVida notifies In4Suite when a MiVida user changes a record that came from In4Suite. Changes In4Suite sent itself are not echoed back, and historical records aren’t synced.
MiVida needs the URL and credentials of the In4Suite endpoint that will receive these events. Sync-back stays switched off until then. The format below is MiVida’s proposal; if In4Suite already has an inbound API, send us its specification and we’ll adapt.
Delivery
- POST
<your URL>with a JSON body. - Headers:
Content-Type: application/json,Authorization: Bearer <token you give us>,Idempotency-Key: <event_id>. - Any 2xx response counts as delivered. Anything else, or a 15-second timeout, is retried up to 8 times, about a minute apart.
- Delivery is at-least-once: use
event_idto discard duplicates. Order isn’t guaranteed; compareoccurred_atif order matters.
Envelope
{
"event_id": "0192a1c0-1d2e-7f30-8a41-5b6c7d8e9f00",
"event": "opportunity.assigned",
"occurred_at": "2026-10-07T08:15:31Z",
"data": { ... }
}Events
| event | Sent when | data |
|---|---|---|
opportunity.assigned | A MiVida user assigns or reassigns the opportunity’s agent | opportunity_id, assigned_agent_id (your In4Suite id of the agent; null when the opportunity was unassigned; omitted when the agent isn’t mapped to In4Suite, so keep your current value), assigned_at |
opportunity.status_changed | The MiVida lead moves along the pipeline | opportunity_id, status, status_label, status_remarks, status_updated_at |
lead.updated | A MiVida user changes the customer’s contact details | lead_id plus only the changed fields among customer_name, customer_phone, customer_email, customer_country_code |
MiVida pipeline statuses, in order
- opportunity
- site_visited
- booked
- sold
- loo_executed
- min_deposit_paid
- afl_requested
- afl_issued
- afl_executed
LOO is the Letter of Offer; AFL is the Agreement for Lease.
11Error messages
| Field | Message (abridged) | Fix |
|---|---|---|
| any required field | <field> cannot be blank. | Send a value |
customer_email | customer_email is not a valid email address. | |
customer_email / customer_phone | Email already exists. / Phone already exists. | Another MiVida customer holds it |
customer_email / customer_phone | … already belongs to In4Suite lead "X". | Two of your leads share this value |
customer_country_code | … is not a known ISO2 country code or dial code. | Use ISO2 |
created_at | … must be an ISO 8601 date-time … / … cannot be in the future. | |
lead_id | In4Suite lead "X" has not been pushed to MiVida yet … | Push the lead, then retry |
lead_id | An opportunity cannot be moved to another lead … | Not supported |
project_id | … is not mapped to any MiVida project (In4Suite Project ID). | Ask MiVida to set the In4Suite Project ID on the project |
area | … does not match any MiVida unit size id. / … belongs to a different project … | Check the master data sheet |
unit_type / source_category / source | … is not a MiVida unit type / source category / source. | Check the master data sheet |
source | … does not belong to source category "X". | |
source | source_category changed: the stored source "X" does not belong to "Y" … | Send a source from the new category, or "" |
assigned_agent_id | No MiVida employee is mapped to In4Suite id "X". | Ask MiVida to set the In4Suite ID on the employee |
budget | budget format message | 1500000 or 1500000-1800000 |
opportunity_id | This opportunity was deleted in MiVida … | Contact MiVida |
opportunity_id / lead_id (batch) | MiVida could not process this item because of an internal error … | Retry later; contact MiVida if it persists |
12Go-live checklist
- MiVida provides
client_id/client_secretfor each environment (staging, production). - MiVida sets the In4Suite Project ID on every project In4Suite will send opportunities for.
- MiVida sets the In4Suite ID on every employee In4Suite assigns opportunities to.
- In4Suite verifies the master data mapping (unit sizes, unit types, source categories and sources) against the MiVida Master Data sheet.
- Dry run of the historical load on staging; review the
faileditems. - In4Suite provides the sync-back URL and token.
- Production historical load: leads, then opportunities.
- Switch to live pushes.
Coming next
- Bookings (next phase): In4Suite bookings will update MiVida pipeline stages, including for historical records.
- Activity history (calls, notes, site visits) is out of scope for this phase.
13Changes in 1.1
What changed since version 1.0. The first three rows change what you send or receive.
| Area | 1.0 | 1.1 |
|---|---|---|
project_id (request) | MiVida project code or id | Your In4Suite project id, mapped on the MiVida project. Codes and MiVida ids are no longer accepted. |
project_id (response) | MiVida project id | Your In4Suite project id, as you send it. MiVida’s id moves to mivida_project_id. |
assigned_agent_id | Employee code | Your In4Suite agent id, mapped on the MiVida employee. Employee codes are no longer accepted. Also used in responses and in opportunity.assigned. |
opportunity.assigned | assigned_agent_id always present | Omitted when MiVida’s agent isn’t mapped to In4Suite; null only when the opportunity was unassigned. |
unit_type, source_category, source | MiVida value or label, case-insensitive | Your In4Suite names, ignoring case, spaces and separators. MiVida’s lists now include In4Suite’s. The same source may exist under several categories. |
salutation | Stored as sent | Your In4Suite salutation, matched to MiVida’s (Dr. → Dr). Unknown ones are still stored as sent. |