Skip to main content

Lead Entry API ✔

Use the Lead Entry API to create leads, clients, and tasks in SingleOps from an outside system and cut duplicate data entry.

In this article:

⚠️ IMPORTANT: The SingleOps Support team will provide one API token per account/request, but it is up to the account to create and maintain the connection. We offer the following documentation, but cannot offer support related to creating, testing, or resolving issues with the use of the API token.

What is the Lead Entry API?

The Lead Entry API lets you create new leads in SingleOps directly from an outside system — for example, your website, a marketing form, or another CRM. Instead of re-keying the same information in two places, leads flow straight into SingleOps, which cuts down on duplicate data entry and keeps your team moving faster.

This is a Lead Entry API only. It's scoped to creating portal leads (and, optionally, a task alongside them). It is not a general-purpose job-creation or read/write API, and there is no native Zapier integration — the connection is a developer integration you build and maintain.

Because it's a developer integration, you'll need someone technical on your team to set it up and maintain it. If that's not something you have in-house, our Client Portal for Lead Entry does the same job directly from your website with no code required.

⚠️ Leads created through this API follow the Customer Portal workflow, which means a client or location must be manually confirmed in SingleOps before it's considered valid.


Before you start

You'll need two things in place:

  1. A technical resource on your team. SingleOps provides the steps to request an API token, but connecting the integration, troubleshooting it, and maintaining it going forward all happen on your end.

  2. An API access token. Email support@singleops.com to have a new API User created on the account where you want leads to land. You'll receive an email address and an API token — keep both handy, since every request uses them. This API user does not incur any additional cost.


How it works

Creating a lead is a short sequence of requests. At a high level:

  1. Look up reference IDs — grab the operation and custom input IDs your account uses. These identify which division a lead belongs to and any extra fields you track. You only need to do this occasionally (and can cache the results).

  2. Search for the client — check whether the lead matches an existing client by email or phone.

  3. Create the lead — send the lead itself, either attached to the existing client you found or as a brand-new client. Optionally, you can create a task at the same time.

The rest of this article walks through each step with the exact endpoints and examples.


Authentication

Credentials

Every request is authenticated with the two values from your API user:

user_email:
user_token:

For most requests these go in the message body. GET requests can also accept them as URL parameters.

Account groups

Each account has its own unique API user. An API user can only create leads on the account it belongs to — even if the underlying person has access to other accounts in the same account group, the API user cannot make calls to those other accounts.

Example: GET request with URL parameters

curl -X 'GET' \
'https://app.singleops.com/api/v1/custom_inputs?user_email=&user_token=' \
-H 'accept: application/json'

Fill in your user_email and user_token, and your request will look like this:

curl -X GET \
"https://app.singleops.com/api/v1/custom_inputs?user_email=accountname%2Bapiuser@singleops.com&user_token=YOUR_TOKEN_HERE" \
-H "accept: application/json"

Important: encode the + in your email

API user emails are usually set up with plus-addressing, like accountname+apiuser@singleops.com. When you put that email in a URL, the + must be URL-encoded as %2B.

This matters because a raw + in a URL is read as a space. If you paste your email in unencoded:

?user_email=accountname+apiuser@singleops.com

the server actually receives accountname apiuser@singleops.com (with a space), authentication fails, and you'll get a confusing error. Encoding the + as %2B fixes it:

?user_email=accountname%2Bapiuser@singleops.com

The @ does not need to be encoded — a literal @ is accepted in the query string. The + is the only character here that has to be changed.

Testing in your browser

For quick testing of GET requests, you can use your normal logged-in session (your user cookie) to hit the API endpoints, with two conditions:

  • Requests must be made over HTTPS.

  • You must be viewing the correct account. If you belong to an account group and are currently looking at a different account in-app, switch back to your "home" account before you can access its data.


Step 1: Look up reference IDs

Two IDs are required on every lead: an operation ID and, if you use them, custom input IDs. We recommend caching these, but you can also fetch them fresh for each lead.

Operations

An operation is the division a lead belongs to. It's required on every lead, though it can be reassigned later if it's mis-classified.

Endpoint: GET https://app.singleops.com/api/v1/operations No additional parameters needed.

Response:

[
{
"id": 1234,
"name": "Main",
"active": true,
"tax_location_id": null,
"display_name": null,
"location_id": null,
"location": {}
},
{
"id": 1235,
"name": "Other",
"active": true,
"tax_location_id": null,
"display_name": null,
"location_id": null,
"location": {}
}
]

Custom inputs

Custom inputs are extra fields your account defines to capture additional information on leads, jobs, clients, items, and more — for example, a "Lead Source" field.

Endpoint: GET https://app.singleops.com/api/v1/custom_inputs

Response:

[
{
"id": 1234,
"name": "Main",
"active": true,
"tax_location_id": null,
"display_name": null,
"location_id": null,
"location": {}
},
{
"id": 1235,
"name": "Other",
"active": true,
"tax_location_id": null,
"display_name": null,
"location_id": null,
"location": {}
}
]

You can ignore dropdown_options. It lists the choices a custom input supports, but since your lead source will typically be the same value every time, you won't need it.


Step 2: Search for an existing client

Before creating a lead, search for a matching client by email or phone so you don't create a duplicate.

Endpoint: GET https://app.singleops.com/api/v1/clients/search_by_field

Parameters:

search_field: "email" or "phone"
search_term: [the email address or phone number]

A few things to know:

  • The search is a prefix search. Clients sometimes store multiple phone numbers or extensions in one field, so a prefix match helps catch those.

  • A search can return anywhere from 0 to 50 results. If there are more, use the page parameter to page through them.

Why this step matters: the API does not merge records.
• If you pass client_id, the lead attaches directly to that existing client.
• If you leave client_id out, the lead is held against a shared placeholder client ("Anonymous Portal Client"), and the details you sent under portal_lead stay on the lead itself. A real client is created only when someone confirms the lead in SingleOps, and it's created fresh, with no matching against your existing clients.
Skipping this search doesn't fail immediately, it defers the problem: every unconfirmed lead looks unattached in the app, and confirming them creates duplicates for customers you already have.

Response (one match shown):

[
{
"id": 12345,
"account_id": 123,
"name": "Example Client",
"active": true,
"email": "support@singleops.com",
"phone": "555-555-5555",
"first_name": "Test",
"last_name": "Search",
"company_name": "",
"portal_active": true,
"on_hold": false
}
]

(The full response includes many more fields; the ones above are the most relevant for identifying a match.)

Once you've searched, you'll know which path to take in the next step: attach the lead to the existing client you found, or create it with a new client.


Step 3: Create the lead

All leads are created by sending a POST request with a JSON body to:

https://app.singleops.com/api/v1/jobs

Choose the version below that matches your situation.

Required fields: every lead needs operation_id, plus a way to identify the client — either a client_id (existing client) or client details under portal_lead (new client). Everything else is optional.

About visit_stage_id: this sets the stage the lead lands in. Use 8 — the Lead stage. Stage IDs are the same across all SingleOps accounts. If you leave visit_stage_id out, the request won't create a lead, so include it.

For an existing client

Include the client_id you found in Step 2.

{
"user_email": "",
"user_token": "",
"job": {
"client_id": 12345,
"operation_id": 123,
"name": "Example Lead",
"job_notes": "Here are notes that are internal.",
"client_notes": "Here are notes that the client can see.",
"crew_notes": "Here are notes that are on the work order.",
"custom_input_values": [
{ "custom_input_id": 123, "value": "Email" }
]
},
"portal_lead": {
"address": "101 Test Ave",
"city": "Atlanta",
"us_state": "GA",
"zip": "30339"
},
"visit": {
"visit_stage_id": 8
}
}

Response — confirms whether the lead was created and returns its ID:

{
"success": true,
"error": null,
"id": 123456
}

For a new client

Leave client_id out of the job object. Instead, put the new client's details — including phone, email, and mobile — under portal_lead.

{
"user_email": "",
"user_token": "",
"job": {
"operation_id": 916,
"name": "Example Lead",
"job_notes": "Here are notes that are internal.",
"client_notes": "Here are notes that the client can see.",
"crew_notes": "Here are notes that are on the work order.",
"custom_input_values": [
{ "custom_input_id": 463, "value": "Email" }
]
},
"portal_lead": {
"first_name": "Example",
"last_name": "Lead",
"company_name": "LeadCo LLC",
"email": "new-lead@example.com",
"phone": "555-555-5555",
"address": "101 Test Ave",
"city": "Atlanta",
"us_state": "GA",
"zip": "30305",
"bill_addr_1": "200 Main St",
"bill_addr_2": "Apt 2B",
"bill_addr_state": "GA",
"bill_addr_postal_code": "30367",
"bill_addr_city": "Marietta"
},
"visit": {
"visit_stage_id": 8
}
}

Creating a lead with a task

To create a task at the same time as the lead, add two things to the body above:

  • Set "job_task_create": true

  • Include a task object

{
"user_email": "",
"user_token": "",
"job": {
"operation_id": 916,
"name": "Example Lead",
"job_notes": "Here are notes that are internal.",
"client_notes": "Here are notes that the client can see.",
"crew_notes": "Here are notes that are on the work order.",
"custom_input_values": [
{ "custom_input_id": 463, "value": "Email" }
]
},
"portal_lead": {
"first_name": "Example",
"last_name": "Lead",
"company_name": "LeadCo LLC",
"email": "new-lead@example.com",
"phone": "555-555-5555",
"address": "101 Test Ave",
"city": "Atlanta",
"us_state": "GA",
"zip": "30305",
"bill_addr_1": "200 Main St",
"bill_addr_2": "Apt 2B",
"bill_addr_state": "GA",
"bill_addr_postal_code": "30367",
"bill_addr_city": "Marietta"
},
"visit": {
"visit_stage_id": 8
},
"job_task_create": true,
"task": {
"name": "Task name",
"notes": "Internal Notes from Lead",
"on_site": true,
"start": "2021-10-10 07:00:00",
"stop": "2021-10-10 07:30:00",
"all_day": false
}
}

Response:

{
"success": true,
"error": null,
"id": 148830
}

Appendix: Field reference

Character limits and accepted values for each field.

Job

Field

Limit

name

value: text

limits: 41 characters

custom_input_values.value

value: text

limits: 65,535 characters (currently listed as 4,000)

operation_id

  • value: integer — the operation ID from GET/operations

  • extra info: required on every lead

client_id

value: integer — the client ID from the Step 2 search

sales_rep_ids

  • value: array of integers (user IDs)

  • extra info: sets the lead's owner. If job_task_create is true, these same users are also assigned to the task that gets created — and if no sales reps are set, the task falls back to the job's assignees instead.

custom_input_values.custom_input_id

  • value: integer — the custom input ID from GET /custom_inputs

  • extra info: required on every entry in the custom_input_values list. The table documents the value but never the ID that has to travel with it.

job_notes

  • value: text

  • limits: 65,535 characters

  • extra info: internal notes

client_notes

  • value: text

  • limits: 65,535 characters

  • extra info: the notes the client can see

crew_notes

  • value: text

  • limits: 65,535 characters

  • extra info: the notes that appear on the work order

Visit

Value

Limit

visit_stage_id

  • value: integer — always 8

  • required to create a lead

tag_ids

  • value: array of integers

  • optional. Handy for tagging leads by source or priority at creation.

Portal lead

Field

Limit

first_name

255 characters

last_name

255 characters

company_name

255 characters

email

255 characters

phone

255 characters

mobile

255 characters

address

255 characters

city

255 characters

us_state

value: text

5 characters (state / province codes)

zip

255 characters

middle_name

value: text

limits: 255 characters

bill_addr_1, bill_addr_2, bill_addr_3, bill_addr_city, bill_addr_state, bill_addr_postal_code

value: text

limits: 255 characters each

Task

Field

Value

job_task_create

true or false

task.name

value: text

limits: 255 characters

task.notes

value: text

limits: 65,535 characters

task.on_site

true or false

task.start

DateTime

task.stop

DateTime

task.all_day

true or false

Did this answer your question?