Partner API

Jobs

Post a job to a company you manage, publish it, and move it through draft, live, closed and archived.

Jobs belong to a company your app created. If you have not made one yet, start with Companies.

Lifecycle

FieldTypeDescription
draftNot visible

Created but not published. Edit freely.

activeLive

On the Tabbio job board, matched to candidates, accepting applications.

closedOff the board

No new applications. Existing candidates stay readable, and it can be published again.

archivedHidden

Out of everyday management. Still readable by id, and no longer editable.

The transitions are publish (draft or closed to active), close (active to closed) and archive (draft or closed to archived). Archiving is not a shortcut for unpublishing: a live job has to be closed first.

Post a job

POSThttps://server.tabbio.com/v1/companies/{companyId}/jobs

Requires jobs:write. The body is grouped into sections rather than flat: basics, content, compensation, eligibility, questions, distribution and confidential.

saveAs decides what happens on create. It defaults to draft; send "live" to publish in the same call, which means the body has to satisfy the full publish contract straight away.

Terminal
curl -X POST https://server.tabbio.com/v1/companies/$COMPANY_ID/jobs \
  -H "Authorization: Bearer $TABBIO_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: crm-req-4412-ops-manager" \
  -d '{
    "saveAs": "live",
    "basics": {
      "title": "Regional Operations Manager",
      "department": "Operations",
      "country": "United Arab Emirates",
      "city": "Dubai",
      "workMode": "onsite",
      "employment": "full_time",
      "seniority": "senior"
    },
    "content": {
      "summary": "Own the regional operations function end to end across the Gulf network.",
      "responsibilities": ["Run the daily operation across three hubs"],
      "requirements": ["Seven years running regional logistics operations"],
      "benefits": ["Health cover"]
    },
    "compensation": {
      "min": 25000,
      "max": 35000,
      "currency": "AED",
      "period": "monthly",
      "visible": true
    },
    "questions": [],
    "distribution": "tabbio_and_company"
  }'

basics

FieldTypeDescription
titlerequiredstring

The role. Keep it to the job title people search for, without a location or a requisition number.

workModerequiredstring

One of onsite, hybrid, remote

employmentrequiredstring

One of full_time, part_time, contract, internship

seniorityrequiredstring

One of entry, mid, senior, lead, executive

departmentstring

Free text.

countrystring

Required in practice to publish, and to match the job to candidates.

citystring

Where the work happens.

content

FieldTypeDescription
summaryrequiredstring

Markdown, up to 8000 characters. This is what a candidate reads first.

responsibilitiesrequiredstring[]

One line each.

requirementsrequiredstring[]

One line each.

niceToHavestring[]
benefitsstring[]

compensation

FieldTypeDescription
currencyrequiredstring

ISO 4217, for example AED or SAR.

periodrequiredstring

One of monthly, yearly

visiblerequiredboolean

Whether the range is shown publicly. A hidden range is still used for matching.

minnumber

Greater than zero.

maxnumber

Greater than zero.

distribution and confidential

distribution is required and decides where the job appears.

FieldTypeDescription
tabbio_and_companystring

The Tabbio job board and the company career page.

company_page_onlystring

The career page only, not the board.

hiddenstring

Neither. Reachable only by direct link.

confidential takes { enabled, publicLabel } and hides the employer name behind a label such as "A logistics group in Dubai".

eligibility and questions

eligibility narrows who the job is matched to: candidateLocation, experienceYears, language, workEligibility, and a workforceProgramme block for Emiratisation and Saudization. Each is an enum; the reference page lists the values.

questions is up to 30 screening questions. Each has a prompt, a type (yes_no, multiple_choice, short_answer, long_answer, number), a required flag, and options for a multiple choice. The answers come back on every application.

Idempotency-Key

Send an Idempotency-Key header, at most 80 characters, and a retry with the same key returns the job the first attempt created instead of posting a duplicate. Keys are namespaced per company, so the same key against two companies is two jobs. The header wins over an idempotencyKey in the body.

Derive it from your own record, so a retry after a network timeout naturally produces the same key.

Retrying without a key double posts

A timed-out request may well have succeeded. Without the header there is no way for the second attempt to know that, and you end up with two identical live postings.

Publish, close, archive

POSThttps://server.tabbio.com/v1/jobs/{jobId}/publish
POSThttps://server.tabbio.com/v1/jobs/{jobId}/close
POSThttps://server.tabbio.com/v1/jobs/{jobId}/archive

All three need jobs:write and take no body.

Terminal
curl -X POST https://server.tabbio.com/v1/jobs/$JOB_ID/publish \
  -H "Authorization: Bearer $TABBIO_API_KEY"

These are not idempotent

Publishing a job that is already live answers 400, not 200. So does closing a job that is not live, and archiving one that is. Check status first, or treat the 400 as the no-op it describes rather than retrying it.

Publishing requires the publish contract to be satisfied: location, salary and screening rules. A draft that is missing them answers 400 with the field named.

Update a job

PATCHhttps://server.tabbio.com/v1/jobs/{jobId}

Requires jobs:write. Send the sections you are changing, in the same shape as the create. A live job stays live and must keep satisfying the publish contract; an archived job cannot be edited at all.

Terminal
curl -X PATCH https://server.tabbio.com/v1/jobs/$JOB_ID \
  -H "Authorization: Bearer $TABBIO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "basics": {
      "title": "Regional Operations Manager",
      "department": "Supply chain",
      "country": "United Arab Emirates",
      "city": "Dubai",
      "workMode": "onsite",
      "employment": "full_time",
      "seniority": "senior"
    }
  }'

basics is validated as a whole, so send the whole section rather than the one field you are changing.

List a company's jobs

GEThttps://server.tabbio.com/v1/companies/{companyId}/jobs

Requires jobs:read. Newest first, page based. Archived jobs are excluded unless you ask for them with status=archived.

FieldTypeDescription
statusstring

Filter by lifecycle status.

One of draft, active, closed, archived

pageinteger

1 based page number. Defaults to 1.

pageSizeinteger

Items per page, 1 to 100. Defaults to 25.

Terminal
curl "https://server.tabbio.com/v1/companies/$COMPANY_ID/jobs?status=active&pageSize=100" \
  -H "Authorization: Bearer $TABBIO_API_KEY"
200 OK
{
  "data": [
    {
      "id": "job_6p1q8r3s",
      "companyId": "cmp_4k8m2n6p",
      "title": "Regional Operations Manager",
      "status": "active",
      "workMode": "onsite",
      "employment": "full_time",
      "seniority": "senior",
      "city": "Dubai",
      "country": "United Arab Emirates",
      "salary": { "min": 25000, "max": 35000, "currency": "AED", "period": "monthly" },
      "salaryVisible": true,
      "distribution": "tabbio_and_company",
      "screeningQuestions": [],
      "applicationCount": 0,
      "postedAt": "2026-09-01T10:30:00.000Z",
      "createdAt": "2026-09-01T10:22:00.000Z",
      "updatedAt": "2026-09-01T10:30:00.000Z"
    }
  ],
  "error": null,
  "meta": { "page": 1, "pageSize": 100, "total": 1, "totalPages": 1 }
}

postedBy and the stored idempotency key are never exposed.

Read one job

GEThttps://server.tabbio.com/v1/jobs/{jobId}

Requires jobs:read. Works in every state, including archived.

Writing a description that performs

content.summary is markdown and it is what a candidate reads before deciding to apply. Three things move the numbers more than anything else:

  • A visible salary range. Set compensation.visible to true. Postings without one are skipped in a market where most have it.
  • The work mode, stated plainly. "Onsite, Dubai" beats "flexible working".
  • A short requirements list. Ten bullets of nice-to-haves reads as a wish list and filters out the people who would have been fine. Put those in niceToHave.