Skip to main content

Lead Assignment, Filters and Fields

Once leads arrive, your team routes and sorts them. With the Public API your CRM or automation can do the same: assign each lead to the right teammate, list a teammate's leads or those matching a custom field, pin the ones that need attention, and shape the agent's lead form, which also decides what the AI collects when it saves a lead. Every endpoint lives under one agent: /v1/agents/{agentId}/….

How do I assign a lead to a teammate?​

POST the teammate's user_id as assigned_to to the lead's assign endpoint. The teammate becomes the lead's Owner in the dashboard, and the lead's conversation is assigned to them too. The teammate must be able to open the agent.

curl -X POST https://api.mojeeb.app/v1/agents/YOUR_AGENT_ID/leads/LEAD_ID/assign \
-H "Authorization: Bearer mk_live_YOUR_KEY_HERE" \
-H "Content-Type: application/json" \
-d '{ "assigned_to": "TEAMMATE_USER_ID", "reason": "Handles the Riyadh branch." }'

Find the user_id with List teammates for an agent: only teammates whose can_open_agent is true can be assigned. Anyone else returns 422 assignee_not_allowed, and nothing changes. reason is optional and is kept with the assignment in Mojeeb's records. The response is the lead with its new assigned_to. See the Assign or unassign a lead reference.

How do I unassign a lead?​

POST assigned_to as null to the lead's assign endpoint. The lead then shows No owner in the dashboard. The field is required, so an empty body is refused instead of unassigning a lead by accident.

curl -X POST https://api.mojeeb.app/v1/agents/YOUR_AGENT_ID/leads/LEAD_ID/assign \
-H "Authorization: Bearer mk_live_YOUR_KEY_HERE" \
-H "Content-Type: application/json" \
-d '{ "assigned_to": null }'

How do I list a teammate's leads, or the unassigned ones?​

Add assigned_to to the lead list: a teammate's user_id, or unassigned for the leads assigned to no one. It combines with the other filters, such as status, and a lead is listed only when it matches all of them.

curl "https://api.mojeeb.app/v1/agents/YOUR_AGENT_ID/leads?assigned_to=unassigned&status=new" \
-H "Authorization: Bearer mk_live_YOUR_KEY_HERE"

me isn't taken, because a key isn't a person: it and any other value return 422 invalid_request_body with param assigned_to. See the List leads reference.

How do I filter leads by a custom field?​

Pass custom_fields: a URL-encoded JSON object that maps a field's key to a clause, an op with its values. With curl, -G and --data-urlencode do the encoding for you.

curl -G https://api.mojeeb.app/v1/agents/YOUR_AGENT_ID/leads \
-H "Authorization: Bearer mk_live_YOUR_KEY_HERE" \
--data-urlencode 'custom_fields={"city":{"op":"eq","values":["Cairo"]},"visit_date":{"op":"between","values":["2026-10-01","2026-11-01"]}}'
opvaluesA lead matches when the field
eqOneEquals the value
inOne or moreEquals any of the values
ilikeOneContains the value, ignoring case; % and _ act as wildcards
betweenTwoIs from the first value, included, to the second, excluded
is_nullNoneHas no value

Values are compared with the stored value as text. Write dates as YYYY-MM-DD so between orders them correctly, and don't expect numbers to compare by size. Clauses combine with AND, and a lead that has no value under a key matches only is_null there. The number of keys and values per request is limited; see the List leads reference.

How do I pin or unpin a lead?​

PATCH the lead with pinned: true pins it for everyone on the agent, as the dashboard's Pin does, and false unpins it. Pinned leads come first, on the first page of the lead list.

curl -X PATCH https://api.mojeeb.app/v1/agents/YOUR_AGENT_ID/leads/LEAD_ID \
-H "Authorization: Bearer mk_live_YOUR_KEY_HERE" \
-H "Content-Type: application/json" \
-d '{ "pinned": true }'

Sending the state a lead is already in changes nothing, so a pinned lead keeps its place. Later pages of the list never contain pinned leads, so keep fewer leads pinned than the limit you list with. See the Update a lead reference.

How do I add a lead field?​

POST a key, a label and a type to the agent's lead fields. The field is added after the agent's other custom fields, in the lead form and as a column of the dashboard's Clients table, and the AI's Capture Lead tool starts collecting it in the background, usually within a minute.

curl -X POST https://api.mojeeb.app/v1/agents/YOUR_AGENT_ID/lead-fields \
-H "Authorization: Bearer mk_live_YOUR_KEY_HERE" \
-H "Content-Type: application/json" \
-d '{ "key": "preferred_branch", "label": "Preferred branch", "type": "enum", "options": [ { "value": "riyadh", "label": "Riyadh" }, { "value": "jeddah", "label": "Jeddah" } ] }'

The label tells the AI what to ask for, so write it the way you would explain the field to a new teammate. "required": true makes the AI treat the field as needed before it saves a lead. An enum field takes its choices in options, and other types take none. The key is where each lead keeps the value in custom_fields, and it can't change later.

A key Mojeeb reserves, such as name, status or source, returns 422 invalid_request. Any other key the agent already has, built-in fields such as notes included, returns 409 field_key_taken, and an agent with no room for another custom field returns 422 lead_field_limit_reached. The key format, the types and the limits are in the Add a lead field reference.

How do I change a lead field?​

PATCH a custom field by its key to change its label, whether it is required, or its options. Send only what changes. The key and the type stay; for another type, add a new field. The AI follows the change in the background, usually within a minute.

curl -X PATCH https://api.mojeeb.app/v1/agents/YOUR_AGENT_ID/lead-fields/preferred_branch \
-H "Authorization: Bearer mk_live_YOUR_KEY_HERE" \
-H "Content-Type: application/json" \
-d '{ "options": [ { "value": "riyadh", "label": "Riyadh" }, { "value": "jeddah", "label": "Jeddah" }, { "value": "dammam", "label": "Dammam" } ] }'

options replaces every choice, in the order you send them. A choice whose value stays keeps what the dashboard set on it, such as its colour, and leads keep values you remove. The key in the path is matched exactly, case included, and a key the agent has no field for returns 404 not_found. Built-in fields, the lead statuses among them, can't be changed through the API (422 system_field). See the Change a lead field reference.

How do I remove a lead field?​

DELETE a custom field by its key. It leaves the agent's lead form permanently, and the AI stops collecting it in the background, usually within a minute. The response is 204 with no body.

curl -X DELETE https://api.mojeeb.app/v1/agents/YOUR_AGENT_ID/lead-fields/preferred_branch \
-H "Authorization: Bearer mk_live_YOUR_KEY_HERE"

Leads keep the values they had under the key: the API still returns them in custom_fields, the dashboard's table stops showing them, and they show again if you add a field with the same key. Built-in fields can't be removed (422 system_field). See the Delete a lead field reference.

Common questions​

Who is recorded as making an assignment through the API?​

The person who created the key. Mojeeb records the assignment as theirs, made by hand, the same way it records them as the author of a key's notes.

What does 422 assignee_not_allowed mean?​

The teammate can't open the agent: they are limited to other agents, they left your organization, or the id isn't one of your teammates. Choose someone whose can_open_agent is true, or give them the agent in the dashboard first; see Team Management. If Mojeeb couldn't check the teammate's access, the response is 503 access_unavailable instead: retry with backoff.

Why does assigned_to=me return 422?​

A key isn't a person, so it has no leads of its own. Use the user_id of the teammate you mean, from List teammates for an agent.

Can I assign a lead to the teammate it already has?​

Yes. The request succeeds and the lead stays theirs, so your integration can send an assignment without checking first.

Why does my custom-field filter return nothing?​

Check four things. The key must match the field's key exactly, case included. eq and in match the stored text exactly, so use ilike to ignore case. between leaves out its second value, so to include the last day of a month, end at the first day of the next one. And numbers compare as text.

Can I filter numbers by size?​

No. Values compare as text, so between doesn't follow numeric order. Use eq or in with the exact values, or filter numbers in your own system.

Can I change a field's type or key?​

No. Add a new field with the key and type you need. A field you delete and add again under the same key with another type finds the old values as they were, unconverted.

Can I change the lead statuses through the API?​

Not yet. The statuses are the options of the built-in status field, and built-in fields can't be changed through the API (422 system_field). Edit them in the dashboard with Edit Statuses. Moving a lead from one status to another works through the API; see Contacts, Notes and Leads.

Why did creating a field return 409?​

The agent already has a field with that key, built-in fields included, and nothing was created. A create you retried after a lost response lands here too: list the lead fields and carry on.

Why doesn't the AI collect a field I added?​

Capture Lead picks up field changes only while it is switched on. If it is off, switch it on from its card on the Tools page, then open Edit Columns on the Clients page and save once, as Capture Lead explains.

Can I pin as many leads as I like?​

Yes, there is no cap, but the lead list returns pinned leads on its first page only, in the API and in the dashboard alike. Pin only what needs attention now, and unpin a lead once it is handled.

Can I delete a lead?​

Yes: DELETE /v1/agents/{agentId}/leads/{leadId} removes it permanently, with no undo, and a second delete of the same lead returns 404 not_found. The contact, its notes and the conversation stay. To take a lead out of your team's open work without losing it, move it to a final status instead. See Contacts, Notes and Leads.