BetterJobs has no native Zapier app. You use two built-in apps:
- Webhooks by Zapier to call the API and to catch events.
- Code by Zapier to check the event signature.
Search jobs with a Custom Request
Section titled “Search jobs with a Custom Request”-
Add an action step: Webhooks by Zapier → Custom Request.
-
Set:
Field Value Method POSTURL https://api.betterjobs.cc/v1/jobs/searchHeaders Authorization=Bearer bj_live_...,BetterJobs-Version=2026-10-01,Content-Type=application/json -
Paste the Data field:
{"filters": {"company_domain_or": ["acme-robotics.example"],"title_or": ["Head of RevOps", "Revenue Operations"],"posted_within_days": 30},"waterfall": {"strategy": "cheapest_first","max_credits": 5},"limit": 5}Replace
acme-robotics.example(a fictional domain) with a field mapped from the trigger, for example a company domain from your CRM. -
Test the step. Zapier parses the JSON response into fields.
Zapier flattens the data array: each job field becomes a list, such as all titles joined. For one job per row, keep limit small and use the first value. For one action per job, add Looping by Zapier on the job id list.
Map metadata.credits_charged into a log column so you can see what each run cost. A run with no matching jobs returns data: [] and costs 0 credits.
Zapier is a poor fit for paging through hundreds of jobs. For large pulls, use an async search with webhook_url set to a Zapier catch hook, or use n8n or code.
Receive webhooks
Section titled “Receive webhooks”BetterJobs POSTs one event per request and signs it with the BetterJobs-Signature header (t=<unix>,v1=<hex>). v1 is the HMAC-SHA256 of <t>.<raw body> with your endpoint secret. Check it before the Zap acts.
1. Catch the raw hook
Section titled “1. Catch the raw hook”- Create a Zap with the trigger Webhooks by Zapier → Catch Raw Hook. Use Raw, not the plain Catch Hook: the signature covers the exact body bytes, and the plain trigger parses them first.
- Copy the webhook URL. Use it as
webhook_urlinPOST /v1/watchesorPOST /v1/searches. - Send a test event.
POST /v1/webhooks/replayre-sends an existing event to its endpoint. - In the test data, find the raw body field and the
BetterJobs-Signatureheader field.
Zapier responds 200 as soon as it catches the request, which meets BetterJobs’ 10-second deadline.
2. Verify in a Code step
Section titled “2. Verify in a Code step”Add Code by Zapier → Run Python (or Run JavaScript). Set the Input Data:
| Key | Value |
|---|---|
raw_body |
The raw body field from the trigger |
signature |
The BetterJobs-Signature header field from the trigger |
secret |
Your endpoint secret |
The secret lives in the step’s input field, not in the code.
import hashlibimport hmacimport jsonimport time
TOLERANCE_SECONDS = 300
parts = dict(p.split("=", 1) for p in input_data["signature"].split(","))signed = f'{parts["t"]}.{input_data["raw_body"]}'.encode()expected = hmac.new(input_data["secret"].encode(), signed, hashlib.sha256).hexdigest()
fresh = abs(time.time() - int(parts["t"])) <= TOLERANCE_SECONDSvalid = fresh and hmac.compare_digest(expected, parts["v1"])event = json.loads(input_data["raw_body"]) if valid else {}
output = { "valid": valid, "type": event.get("type"), "event_id": event.get("id"), "job_id": event.get("data", {}).get("job", {}).get("id"),}const crypto = require('crypto');
const TOLERANCE_SECONDS = 300;
const parts = Object.fromEntries(inputData.signature.split(',').map((p) => p.split('=')));const expected = crypto .createHmac('sha256', inputData.secret) .update(`${parts.t}.${inputData.raw_body}`) .digest('hex');
const a = Buffer.from(expected, 'hex');const b = Buffer.from(parts.v1 ?? '', 'hex');const fresh = Math.abs(Date.now() / 1000 - Number(parts.t)) <= TOLERANCE_SECONDS;const valid = fresh && a.length === b.length && crypto.timingSafeEqual(a, b);const event = valid ? JSON.parse(inputData.raw_body) : {};
output = { valid, type: event.type ?? null, event_id: event.id ?? null, job_id: event.data?.job?.id ?? null,};3. Filter and route
Section titled “3. Filter and route”- Add Filter by Zapier: only continue if
validis true. - Add Paths by Zapier on
type:job.opened: start outbound. It is the only event that should.job.reposted: update the record. Never re-trigger outbound.job.closed: stop sequences tied tojob_id.
Return more fields from the Code step if a later step needs them. The full payload of each event type is in the event catalog.
Failed deliveries
Section titled “Failed deliveries”If a delivery does not get a 2xx within 10 seconds, BetterJobs retries for 24 hours with backoff. After that, replay any event with POST /v1/webhooks/replay. Replays are free. See Webhooks.
Related
Section titled “Related”- Detect hiring changes for watches end to end.
- Clay for per-row enrichment in a table.
- Troubleshooting for signature mismatches and empty results.