SohamRecruit

Developers

Careers API

Integrate SohamRecruit with your own website — read published jobs, accept applications, track candidate status, and receive webhook events.

The Careers API is the integration surface for your branded career site. Use it to list open jobs, submit applications straight into your pipeline, show candidates their status, and get notified of events.

It is intentionally narrow — the careers surface only. It cannot read or write your candidates, clients, or pipeline data. (A general Data API is planned separately.)

Most read endpoints need no authentication. The only credential is the Careers API Key, used server-side to submit applications without a reCAPTCHA challenge. Never put it in browser code.

Base URL https://api.sohamrecruit.com

Authentication

Most read endpoints (listing jobs, the feed, candidate status) need no authentication.

The one credential is the Careers API Key. A company admin generates it under Settings → Careers API Key. Send it as the X-Careers-Api-Key header when submitting applications from your server — a valid key that matches the job’s tenant skips the reCAPTCHA challenge. All other validation (consent, required fields, résumé checks) still applies.

The key is a secret: keep it on your server (e.g. a Cloudflare Pages secret) and never ship it in browser code. It grants no data access beyond application submission. The legacy headers X-Career-Site-Key and X-Partner-Apply-Key are still accepted as aliases.

Quickstart

Two common integrations. The first is a public read you can call from the browser; the second must run on your server so the Careers API Key stays secret.

1. List open jobs (curl)
curl "https://api.sohamrecruit.com/api/jobs/public?tenantId=YOUR_TENANT_ID"
1. List open jobs (browser JS)
const res = await fetch(
  'https://api.sohamrecruit.com/api/jobs/public?tenantId=YOUR_TENANT_ID'
)
const { jobs } = await res.json()
jobs.forEach((j) => console.log(j.title, j.location, j.currency))
2. Accept an application (curl, server-side)
curl -X POST \
  "https://api.sohamrecruit.com/api/jobs/public/JOB_ID/apply" \
  -H "X-Careers-Api-Key: $CAREERS_API_KEY" \
  -F "name=Asha Rao" \
  -F "email=asha@example.com" \
  -F "phone=+919876543210" \
  -F "consent=true" \
  -F "resume=@/path/to/resume.pdf"
2. Accept an application (Node proxy — key stays on your server)
// Runs on YOUR backend. The browser posts the form to you;
// you forward it to SohamRecruit with the secret key.
const form = new FormData()
form.set('name', body.name)
form.set('email', body.email)
form.set('phone', body.phone)
form.set('consent', 'true')
if (body.resume) form.set('resume', body.resume, 'resume.pdf')

const res = await fetch(
  `https://api.sohamrecruit.com/api/jobs/public/${jobId}/apply`,
  {
    method: 'POST',
    headers: { 'X-Careers-Api-Key': process.env.CAREERS_API_KEY },
    body: form,
  },
)
const result = await res.json() // { message, statusUrl }

Jobs

GET/api/jobs/publicNone

List a tenant's open, non-expired jobs (newest first).

  • Query: tenantId (uuid) — scope to one tenant (the normal case).
  • Returns { jobs: PublicJob[] } with a currency per job.
  • Safe to call directly from the browser.
GET/api/jobs/public/{id}None

Get a single open job by id.

  • Returns { job: PublicJob }, or 404 if not found / not open.
GET/api/jobs/feed/{tenantId}None

XML job feed in the LinkedIn/Indeed job-wrapping format.

  • application/xml response.
  • Rate limit: 30 requests / 5 min / IP.

Applications

POST/api/jobs/public/{id}/applyCareers API Key or reCAPTCHA

Submit a candidate application (multipart/form-data).

  • Required fields: name, email, phone, consent. Optional: coverLetter, resume (PDF/DOCX ≤10MB), source.
  • consent must be 'true' (GDPR).
  • With a valid X-Careers-Api-Key, reCAPTCHA is skipped; otherwise a captchaToken is required.
  • 201 → { message, statusUrl }. Errors: 400, 404, 409 (already applied), 429.

Candidate status

GET/api/status/{token}None (token)

Sanitized application status for a candidate.

  • The token is emailed to the candidate on apply (see statusUrl).
  • Returns companyName, jobTitle, appliedAt, and a 4-step status timeline.
  • Never exposes recruiter PII, internal notes, or raw stage names.

Webhooks

Register HTTPS endpoints under Settings → Webhooks to receive signed events. Each delivery is a JSON envelope — { event, tenantId, timestamp, data } — with three headers: X-SohamRecruit-Event, X-SohamRecruit-Signature-256, and X-SohamRecruit-Delivery.

Events: candidate.created, candidate.updated, candidate.stage_changed, placement.created, interview.scheduled, application.received.

Verify the signature (HMAC-SHA256 of the raw request body with your endpoint secret) before trusting a payload. Respond with any 2xx to acknowledge; a non-2xx is retried up to 3 times.

Verify a webhook signature (Node)
import crypto from 'node:crypto'

function verifySignature(rawBody, header, secret) {
  const expected =
    'sha256=' + crypto.createHmac('sha256', secret).update(rawBody).digest('hex')
  return crypto.timingSafeEqual(Buffer.from(header), Buffer.from(expected))
}

Rate limits

Limits are per rolling 5-minute window:

• Public job board reads: 10 requests / 5 min / IP. • Job feed: 30 requests / 5 min / IP. • Apply with a valid Careers API Key: 300 requests / 5 min per tenant. • Apply with a missing/invalid key: 10 requests / 5 min / IP (reCAPTCHA still applies).

A 429 response includes a Retry-After header and a retryAfterSeconds field.

Errors

Errors use a consistent JSON envelope: { "error": "..." } (some also include a human-readable message).

Common status codes: 400 (invalid/missing fields or consent), 404 (not found / invalid token), 409 (duplicate application), 429 (rate limited, with retryAfterSeconds).

Data API — Authentication

The Data API (/api/v1/*) reads and writes your jobs, candidates, applications, and clients. Authenticate with a Data API Key sent as an HTTP Bearer token: Authorization: Bearer sk_live_….

A company admin generates keys under Settings → Data API keys, choosing a scope — read, or read + write. The raw key is shown once; keep it on your server only. Write endpoints (POST / PATCH) require a key with the write scope, otherwise they return 403.

Every key is scoped to your tenant — it can only ever read or write your own data.

Data API — Quickstart

List open jobs (curl)
curl "https://api.sohamrecruit.com/api/v1/jobs?status=open" \
  -H "Authorization: Bearer $DATA_API_KEY"
Create a candidate (curl)
curl -X POST "https://api.sohamrecruit.com/api/v1/candidates" \
  -H "Authorization: Bearer $DATA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"name":"Asha Rao","email":"asha@example.com","skills":"React, Node"}'
Move an application to interview (JS)
const res = await fetch(
  'https://api.sohamrecruit.com/api/v1/applications/' + applicationId,
  {
    method: 'PATCH',
    headers: {
      Authorization: 'Bearer ' + process.env.DATA_API_KEY,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({ status: 'interview' }),
  },
)
const { application } = await res.json()

Data API — Reference

All list endpoints accept `limit` (max 100) + `offset` pagination. Endpoints marked "write" need a key with the write scope.

GET/api/v1/jobsAPI key

List / search jobs (status, q).

POST/api/v1/jobswrite

Create a job.

GET/api/v1/jobs/{id}API key

Get a job.

PATCH/api/v1/jobs/{id}write

Update or close a job.

GET/api/v1/candidatesAPI key

List / search candidates (q, status).

POST/api/v1/candidateswrite

Create a candidate (email-deduped).

GET/api/v1/candidates/{id}API key

Get a candidate.

PATCH/api/v1/candidates/{id}write

Update a candidate.

GET/api/v1/applicationsAPI key

List applications (jobId, status).

GET/api/v1/applications/{id}API key

Get an application.

PATCH/api/v1/applications/{id}write

Update the application stage.

GET/api/v1/clientsAPI key

List / search clients (q, status).

POST/api/v1/clientswrite

Create a client.

GET/api/v1/clients/{id}API key

Get a client.

PATCH/api/v1/clients/{id}write

Update a client.

Questions? Contact support