Skip to content
API v1 preview — endpoints and fields may change before general availability.

Make

How do I call BetterJobs and receive its webhooks in Make?

View .md

BetterJobs has no native Make app. You use three built-in modules:

  • HTTP → Make a request to call the API.
  • Webhooks → Custom webhook to receive events.
  • Tools and a filter to check the event signature.
  1. Add HTTP → Make a request.

  2. Set:

    Setting Value
    URL https://api.betterjobs.cc/v1/jobs/search
    Method POST
    Header Authorization Bearer bj_live_...
    Header BetterJobs-Version 2026-10-01
    Body type Raw
    Content type JSON (application/json)
    Parse response Yes
  3. Paste the request content:

    {
    "filters": {
    "title_or": ["Head of RevOps", "Head of Revenue Operations"],
    "country_code_or": ["DE", "AT", "CH"],
    "posted_within_days": 7
    },
    "waterfall": {
    "strategy": "cheapest_first",
    "max_credits": 100
    },
    "limit": 25
    }

    To use a value from an earlier module, place the cursor inside the quotes and pick the item from the mapping panel, for example a company domain inside company_domain_or.

  4. Add an Iterator after it and map data. Each bundle is now one canonical job.

Map fields such as title, company.domain, apply_url and id into the next module. Store id. Fetching the same job later is free. All fields are in the field dictionary.

  • Add a Break error handler to the HTTP module for 429 and 500. It retries the bundle later. A 500 is safe to retry with the same Idempotency-Key header: you are never charged twice. See Idempotency.
  • 200 with metadata.status = partial is a success. One provider failed or timed out, and you pay only for the jobs returned. See Provider status.
  • A misspelled filter returns 400 unknown_filter. Read error.param in the module output. See Errors.

One request returns at most 100 jobs. For backfills up to 10,000 jobs, use an async search. Make scenarios should not wait in a loop, so split the work in two scenarios.

Scenario A: start the search.

  1. HTTP → Make a request: POST https://api.betterjobs.cc/v1/searches with the same headers. The body takes filters, waterfall and limit (up to 10,000). Add an Idempotency-Key header so a retried run does not start a second search.
  2. Save the returned id (srch_...) in a Data store record with a done flag set to false.

Scenario B: poll and collect. Schedule it every few minutes.

  1. Data store → Search records where done is false.
  2. HTTP → Make a request: GET https://api.betterjobs.cc/v1/searches/{id} with the stored id.
  3. Add a Router on status:
    • queued or running: do nothing. The next run checks again.
    • on_hold: you ran out of credits. The search resumes after a top-up.
    • completed or partial: read results, then set done to true.
    • failed: set done to true and alert someone.
  4. To read results, page with GET /v1/searches/{id}?limit=100&cursor=.... Use a Repeater with Repeats set to ceil(jobs_found / 100). Keep next_cursor in a Set variable with lifetime One execution, and pass it as cursor on the next repeat.

Branch on status, never on the HTTP code. GET /v1/searches/{id} returns 200 for every state. Reading results is free: jobs are charged when the search collects them. See Async searches.

BetterJobs POSTs one event per request to your endpoint and signs it with the BetterJobs-Signature header:

BetterJobs-Signature: t=<unix seconds>,v1=<hex HMAC-SHA256 of "<t>.<raw body>">

You compute the same HMAC with your endpoint secret and compare it to v1.

  1. Add Webhooks → Custom webhook as the first module and create a new hook.
  2. Open Advanced settings. Turn on Get request headers and JSON pass-through. Pass-through keeps the raw body as one text value. The signature covers those exact bytes.
  3. Copy the webhook URL. Use it as webhook_url in POST /v1/watches or POST /v1/searches.
  4. Click Redetermine data structure, then send a test event, for example with POST /v1/webhooks/replay on an existing event id.

Make responds 200 as soon as the webhook accepts the request, which meets the 10-second deadline.

Add Tools → Set multiple variables after the webhook:

Variable Value
sig The BetterJobs-Signature value from the headers array. Use map() on the headers with key value, filtered by name, then first(). Check the exact header name in the webhook output.
t sig split on ,, first part, with t= removed
v1 sig split on ,, last part, with v1= removed
expected sha256() of the text t + . + the raw body value, encoding hex, key = your endpoint secret

Then set a filter on the link to the next module: expected Equal to v1. Bundles that fail the filter stop there.

Add JSON → Parse JSON on the raw body, then a Router on type:

  • job.opened: start outbound. It is the only event that should.
  • job.reposted: update your copy. Never re-trigger outbound.
  • job.closed: stop sequences for that job.
  • search.completed: fetch results with GET /v1/searches/{id}.

What each event means and costs is in the event catalog.

If a delivery does not get a 2xx within 10 seconds, BetterJobs retries for 24 hours with backoff. After that, or after you fix a broken scenario, replay any event with POST /v1/webhooks/replay. Replays are free. See Webhooks.