Blog/tools

The JobNimbus API in 2026: What You Can Actually Build

A practical 2026 guide to the JobNimbus API: OAuth, contacts and jobs endpoints, custom fields by name, external_id idempotency, and an honest list of limits.

JT
Jake Thompson
Roofbird
September 22, 2026

If you are evaluating the JobNimbus API, you are probably asking one question: can I get my own leads into my CRM without a human retyping them? The short answer is yes. The longer answer is that the API is a solid, boring, well-behaved REST interface with a few quirks you need to design around, and one gap that will decide your architecture.

This is written for the person doing the evaluation. Not a sales page, not a tutorial that assumes you already know the answer. Auth, contacts, jobs, custom fields, idempotency, a worked example, and the limits stated plainly.

1. What the API is, structurally

JobNimbus exposes a REST API over HTTPS with JSON request and response bodies. Resources are addressed by type: contacts, jobs, tasks, activities, attachments, and so on. You authenticate as a user in a specific account, and every request is scoped to that account's data.

There are two broad shapes of integration people build:

  • Inbound: something outside JobNimbus creates a contact or a job. This is the roofing case. A scored lead, a web form, a purchased list, a satellite-derived prospect.
  • Outbound: something outside JobNimbus reads jobs, activities or financials to report on them. Less common for roofers, more common for franchise reporting.

If you are still deciding whether the CRM itself is the right place for your leads, that is a different question from whether the API works. The API works. Whether JobNimbus fits your shop is a separate evaluation, and the honest comparison of JobNimbus alternatives for roofers covers that ground without pretending the answer is universal.

2. Authentication: get this right first

The API uses OAuth. You register an application, receive a client ID and secret, and go through an authorization flow where a JobNimbus user in the target account grants your app access. You get back an access token and a refresh token.

Three things that matter in practice:

  1. Tokens expire. Access tokens are short-lived. Your integration needs to refresh proactively, not reactively. If you wait for a 401 and then refresh, you will drop writes under load.
  2. Refresh tokens can rotate. Store the newest refresh token every time you refresh. If you keep an old one and the provider invalidates it, you will be re-authorizing by hand at the worst possible moment.
  3. Scope to one account. If you are building for a single roofing company, this is trivial. If you are building a product that serves many roofers, you are managing one credential set per account, and you need a token store that does not leak across tenants.

For a single-shop integration, the whole auth layer is maybe 80 lines of code. It is not the hard part. The hard part is what you send.

3. Contacts: the record you will write most

A contact in JobNimbus is a person or company record. For roofing, this is the homeowner. Fields you will care about: display name, first and last name, email, phone numbers (with type labels), and the mailing address.

A few practical notes:

  • Phone numbers are typed. Mobile, home, work. If you dump a number into the wrong type, the office staff will notice and complain. Match the type to what you actually know.
  • Addresses are structured. Do not concatenate a single string and hope. Street, city, state, postal code, and country as separate fields, because downstream features like mapping and territory routing depend on them.
  • Contacts and jobs are separate objects. A contact can exist without a job. A job generally references a contact. Decide up front whether your integration creates a bare contact or a contact plus a job in one pass. For a lead that has not been sold yet, a contact plus a lead-stage job is usually what the sales team expects to see.

4. Jobs: where the work actually lives

A job is the container for a project. It carries a status, a type, a source, an assigned salesperson, and a link back to the contact. It is also where most custom fields live, because roofing shops customise jobs far more than they customise contacts.

When you create a job from an external lead, the fields that earn their keep are:

  • Status: put it in whatever stage your pipeline calls "new" or "uncontacted". Do not invent a status; use one that already exists, or your reporting breaks.
  • Source: this is how you will later answer "where did our jobs come from". Set it to something meaningful and consistent. If you write "API" for everything, you have thrown away your attribution.
  • Assigned salesperson: if your integration knows the territory, assign it. Unassigned leads rot.
  • Contact reference: the link back to the homeowner record.

There is a broader question underneath this one: whether pushing scored leads into your CRM automatically is worth building at all, versus exporting and importing by hand. That trade-off is covered properly in whether you can push AI-scored roofing leads into your CRM automatically, and the answer depends mostly on volume and on whether your scoring is stable enough to trust unattended.

5. Custom fields are addressed by name, not by ID

This is the quirk that trips people up.

In many CRMs, custom fields are referenced by a numeric or GUID identifier that you look up once and hardcode. In JobNimbus, custom fields on a record are addressed by their name in the payload. You send a structure where the key is the field's label as configured in that account, and the value is what you want to store.

The consequences:

  1. The field must already exist. You cannot create it through the API. More on that below.
  2. The name must match exactly. Whitespace, capitalisation, punctuation. If the account has a field called Roof Age (est.) and you send Roof Age, it will not land, and depending on how the API handles unknown keys you may get a silent drop rather than an error.
  3. Names are per-account. Two roofing companies with the same CRM will have differently named fields. If you are building a multi-tenant integration, your field mapping is configuration, not code.
  4. Names can change. Somebody in the office renames a field, and your integration starts dropping data. You need a validation step that checks the fields you depend on still exist, and alerts when one disappears.

The practical design: build a mapping layer. Your internal schema on one side, a per-account dictionary of JobNimbus field names on the other, and a startup check that verifies every mapped field resolves before you write a single record.

6. Idempotency with external_id

Networks fail. Webhooks retry. Your own job queue will replay a message after a timeout even though the original request succeeded. Without a guard, you get duplicate contacts and duplicate jobs, and the office staff spend their morning merging records.

The guard is external_id. It is a field you control, where you store your own system's identifier for that record. The pattern:

  1. Generate a stable ID for the lead in your system. Not a random UUID per attempt. The same ID every time you try to push that same lead.
  2. Before creating, search JobNimbus for a record with that external_id.
  3. If it exists, update it. If not, create it.

That is a read-then-write, which is not atomic, so a truly concurrent double-submit can still slip through. In practice, for a roofing integration pushing tens or hundreds of leads a day, a serialised queue per account removes the race entirely. Do not over-engineer this. Do serialise your writes.

The second benefit of external_id is reconciliation. When somebody asks why a lead is missing, you can query by your own ID and get a definitive answer instead of guessing.

7. A worked example: pushing scored leads in

Say you have a list of scored roofs and you want them in JobNimbus as contacts with lead-stage jobs. Here is the shape of the pipeline, in order:

Step 1. Pull the leads. Your source gives you, per house: street address, owner name, phone, email, roof condition, roof age estimate, a need score, a now score, and your own lead ID. If you are sourcing those from satellite imagery rather than buying them, the full Roofbird feature list is where the field-by-field output is documented, including the property record that comes free with every scanned roof.

Step 2. Verify the field mapping. Fetch the account's custom field names, compare against your mapping dictionary, and abort loudly if anything is missing. This step exists because step 6 will silently lose data otherwise.

Step 3. Upsert the contact. Search by external_id first. On miss, create with name, typed phone numbers, email, and structured address. Capture the returned contact ID.

Step 4. Upsert the job. Same external_id discipline. Set status to your new-lead stage, source to something specific like Roofbird - 75216, assign the salesperson if you know the territory, and link the contact ID from step 3.

Step 5. Write the custom fields by name. Roof age, condition, need score, now score, imagery date. Use the exact configured names.

Step 6. Log the result. Store your external_id, the JobNimbus record IDs, the timestamp, and the HTTP status. When something goes wrong three weeks later, this log is the only thing that will save you.

Step 7. Handle failures by class. A 400 means your payload is wrong; retrying will not help, so alert a human. A 429 or 5xx means retry with backoff. A 401 means refresh and retry once.

That is the whole integration. The complexity is not in the API calls. It is in the mapping layer, the idempotency discipline, and the failure handling.

8. The honest limits

There is no endpoint to define custom fields. You can read and write values for fields that exist, but you cannot create the field itself through the API. Somebody has to create it in the JobNimbus UI first, with the exact name your integration expects. For a single shop this is a five-minute setup task you document once. For a product serving many accounts, it is an onboarding step you cannot skip, and it is the single biggest friction point in shipping a JobNimbus integration.

Rate limits exist and are not generous. Treat every integration as if it will be throttled, because under a bulk import it will be. Batch where the API allows it, back off on 429, and never run a tight retry loop.

The API surface changes. Endpoints get added, fields get deprecated. Pin your integration to what you have tested, and re-verify the field mapping on a schedule rather than assuming last quarter's assumptions still hold.

You still have to source the leads. The API moves data between systems. It does not create demand. If your pipeline is empty, a perfect integration just moves nothing faster. That is why the interesting problem is upstream: where the leads come from, and whether they are exclusive to you. The difference between buying shared pay-per-lead leads and generating your own is the difference between an integration that pays for itself and one that just automates a bad unit economics problem.

And the contact details still have to be right. The most common reason a pushed lead goes stale is that nobody called it. The second most common is that the number was wrong. If you are pushing homeowner phone numbers into JobNimbus, they need to be DNC-scrubbed and labelled before they arrive, which is what the homeowner contact and DNC screening layer does: every number checked against the federal registry and tagged clear, DNC, or verify, so your salesperson knows what they are dialling before they dial it. Manual calls only, no auto-dialler, no texting.

9. Should you build it or buy it?

Build it if you have a developer, a stable lead source, and a reason to control the mapping. A single-shop integration is a few days of work and then near-zero maintenance.

Buy it if you do not want to own a token refresh loop and a field mapping dictionary forever. Roofbird ships a native JobNimbus integration that handles the auth, the field mapping and the idempotency for you, and the same exists for AccuLynx. The API is there if you want it. You do not have to want it.

FAQ

Q: Does the JobNimbus API support creating custom fields? A: No. You can read and write values for custom fields that already exist in the account, but there is no endpoint to define a new custom field. The field must be created in the JobNimbus UI first, and your integration must reference it by its exact configured name. Plan for this as a documented onboarding step.

Q: How do I prevent duplicate contacts when pushing leads into JobNimbus? A: Use the external_id field to store your own system's stable identifier for each lead. Before creating a record, search for an existing one with that external_id and update instead of creating. Serialise your writes per account so two concurrent submissions cannot both miss the search and both create.

Q: How are JobNimbus custom fields addressed in API payloads? A: By name, not by numeric ID. You send the field's configured label as the key. That means the name must match exactly, including capitalisation and punctuation, and it means your field mapping is per-account configuration rather than hardcoded logic. Validate that every mapped field still exists before you write records.

Q: What is the biggest risk in a JobNimbus API integration? A: Silent data loss from field name mismatches and duplicate records from missing idempotency. Both are avoidable with a startup validation check on field names and an external_id upsert pattern. Neither is caught by the API for you, so both need to be in your design from day one.

Next steps

  1. Register an app and get a token refresh loop working against a sandbox account before you write anything else.
  2. List the custom fields you need, create them in the JobNimbus UI, and write down their exact names. That list is your mapping dictionary.
  3. Build the external_id upsert for contacts first, jobs second, custom fields third.
  4. Log every write with your ID and their ID. You will need it.
  5. If you would rather skip steps 1 through 4, look at what the native integration already does, and spend your build time on sourcing leads instead of moving them.

New in Roofbird

Now with the homeowner's contact details on every lead

Finding the roof is half the job — you still have to reach the owner. Roofbird now unlocks the homeowner's name, phone, email, and mailing address on any lead, every phone DNC-scrubbed so you know who's safe to call, plus whether they're an owner-occupant or an absentee owner. No skip-tracing tools, no bought lists: find the roof, get the owner, call or mail the same day.

Written by

Jake Thompson

Roofbird

Have a question about anything in this post? Reach the Roofbird team at support@roofbird.ai.

Try Roofbird — 10 free leads in your area

See a sample dashboard for DFW first, no signup needed. Trial loads 10 free pre-scored leads in your own service area.