Authentication
Every request must include your API key in the Authorization header.
Authorization: Bearer nlk_your_api_key_hereAPI keys are issued by the NewLocal team. Contact us or use the employer portal to request one. Keys can have scoped permissions (jobs:write, jobs:read, content:write).
Send Jobs (POST)
Send up to 500 jobs per request. The endpoint deduplicates by content hash (title + employer + location).
POST /functions/v1/receive-job-webhookcurl -X POST "https://<your-project>.supabase.co/functions/v1/receive-job-webhook" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer nlk_your_api_key" \
-H "X-Timestamp: $(date +%s)" \
-H "X-Idempotency-Key: import-2024-01-15-batch1" \
-d '{
"source_name": "My Job Board",
"jobs": [
{
"title": "Senior Backend Engineer",
"company": "TechCorp",
"location": "Toronto, ON",
"country": "Canada",
"country_code": "CA",
"description": "We are looking for...",
"url": "https://example.com/jobs/123",
"salary_text": "CAD 120,000 - 160,000/yr",
"employment_type": "full-time",
"remote_option": "Hybrid",
"experience_level": "Senior",
"tech_stack": ["Node.js", "PostgreSQL", "AWS"]
}
]
}'Request Format
Each job object supports the following fields:
| Field | Type | Required | Description |
|---|---|---|---|
| title | string | Yes | Job title (max 500 chars) |
| company | string | No | Company name (alias: employer) |
| location | string | Yes | City, province/state (max 200) |
| country | string | No | Country name (auto-detected from location) |
| country_code | string | No | ISO 3166-1 alpha-2 code (e.g. CA, US) |
| description | string | No | Full job description (max 50,000 chars) |
| url | string | No | Original job listing URL (alias: source_url) |
| salary_text | string | No | Salary range as text (e.g. 'CAD 80k-120k') |
| employment_type | string | No | full-time, part-time, contract, internship |
| remote_option | string | No | Remote, Hybrid, On-site |
| experience_level | string | No | Entry, Mid, Senior, Executive |
| tech_stack | string[] | No | Technology tags (max 50) |
| requirements | string | No | Requirements (max 10,000 chars) |
| benefits | string | No | Benefits (max 5,000 chars) |
Response Format
{
"success": true,
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"summary": {
"received": 10,
"saved": 8,
"duplicates": 2,
"errors": 0
}
}The request_id is also returned in the X-Request-Id response header.
HMAC-SHA256 Signing
Signing is optional but recommended. It proves the request hasn't been tampered with and prevents replay attacks.
Get current Unix timestamp (seconds)
Send this as X-Timestamp
Concatenate: timestamp + "." + request_body
Compute HMAC-SHA256 using your API key as the secret
Send as X-Signature: sha256=<hex>
Timestamps older than 5 minutes are rejected to prevent replay attacks.
Idempotency
Send the X-Idempotency-Key header to prevent duplicate processing if you retry a failed request.
X-Idempotency-Key: import-2024-01-15-batch1If the same idempotency key is sent again, the original response is returned without reprocessing. Keys are scoped to your API key.
Rate Limits
Each API key has a configurable rate limit (default: 60 requests/minute). Exceeding the limit returns 429 Too Many Requests.
Outbound Webhook Events
Subscribe your endpoint to receive notifications when events happen on NewLocal Jobs. All payloads are HMAC-signed.
| Event Type | Description |
|---|---|
| application.created | A candidate applied to one of your jobs |
| application.status_changed | Application moved to a new pipeline stage |
| job.created | A new job was posted on the platform |
| job.updated | A job listing was modified |
| job.deleted | A job listing was removed |
| candidate.profile_updated | A candidate in your talent pool updated their profile |
Deliveries retry with exponential backoff (1m, 5m, 30m, 2h, 12h). After 5 failures, the endpoint is auto-disabled.
Error Codes
| Code | Meaning |
|---|---|
| 200 | Success — jobs processed |
| 400 | Bad request — invalid JSON, missing fields, too many jobs |
| 401 | Unauthorized — invalid/expired API key or bad signature |
| 403 | Forbidden — API key lacks required scope |
| 413 | Payload too large (max 5 MB) |
| 429 | Rate limited — slow down |
| 500 | Internal server error — retry with backoff |
| 503 | Webhook not configured — contact admin |