Integrating your job platform with Taloo
What the API can do, how the calls fit together, what to build, and what we need from you to go live.
Overview
Recruiters publish vacancies in Taloo. Your platform shows them, collects applications, and reports back how many people looked at each job. Concretely you can:
- Show vacancies and keep them in sync when they change or go offline.
- Accept applications with CV files, delivered straight to the recruiter.
- Report reach: page views and application-form opens show up in the recruiter's statistics.
- Preview drafts in your layout before the recruiter publishes.
Steps 1–4 are required for every integration; within step 2, webhooks are optional. Without step 3 the recruiter's statistics for your platform are empty. Step 5 is recommended.
Build against demo.api.taloo.ch with seeded vacancies and a shared demo key. Nothing to set up on our side.
Send us your channel settings. We create your channel on demo for testing, then on production.
How it fits together
Your platform is a channel in Taloo. Three lanes: the visitor's or recruiter's browser, your server, and Taloo. Every call into Taloo comes from your server with your API key. The only exception is the preview, where the token is the credential. Click a step to pause.
Stage 1 · Explore on demo
| Environment | API | Use for |
|---|---|---|
| Demo | https://demo.api.taloo.ch | Exploring and building. Seeded vacancies, shared demo key. Resets every Monday at 03:00 CET and on every release: vacancies, applications and events are recreated, keys and channels stay. |
| Production | https://api.taloo.ch | Go-live only, with your own key. |
The Example Platform on demo is a complete reference integration: job list, job page with tracking, application form and live preview. Compare your implementation against it.
Rules for every call
- The API key stays on your server. Never ship it in browser JavaScript or an app bundle. Browser events go through your own backend.
- Serve pages from your own database. Each key has a per-minute budget; over it you get
429withRetry-After. Don't call the API per page view. - Reference: GraphQL Playground (vacancies, preview) and Swagger (tracking, applications).
Kick-start with an AI coding agent
Paste this into Claude Code, Cursor or whatever you use. It points the agent at this guide and the machine-readable specs, and lists the steps and rules in the order we'd build them. Fill in the placeholders in angle brackets first.
Show the prompt
I'm integrating our job platform with the Taloo Public API. Taloo is an applicant tracking system: recruiters publish vacancies there, our platform shows them, collects applications and reports views back.
Read these first:
- Integration guide (what to build, in which order, the rules): https://1764.qa.api.taloo.ch/guide
- OpenAPI spec for the REST endpoints (track-action, job-application): https://1764.qa.api.taloo.ch/openapi.json
- GraphQL endpoint (vacancies, preview; introspection is open): https://demo.api.taloo.ch/v1/graphql
- Reference implementation to compare against: https://demo.debug.taloo.ch/example-platform
Our setup:
- Stack: <e.g. Laravel 12 + MySQL, or Next.js + Postgres>
- Job pages: <https://our-site.ch/jobs/:publicUuid>
- Environment variables: TALOO_API_URL=https://demo.api.taloo.ch and TALOO_API_KEY=<demo key from the Example Platform settings bar>. The key must only ever be used server-side.
Build in this order and stop after each step so I can review:
1. Taloo client module. Sends the x-channel-api-key header on every call, server-side only. Handles 429 by honouring Retry-After.
2. Vacancy sync into our own database (pages are rendered from our DB, never from live API calls).
- Initial import paging through vacancies(first: 100, after: endCursor).
- Optional webhook endpoint (only if we register webhook URLs with Taloo) receiving { "event": "publish" | "unpublish", "channelUuid", "publicVacancyUuid" }. Require our secret header, respond 2xx immediately, process in the background: publish = fetch vacancy(publicUuid) and upsert; unpublish = take offline.
- Scheduled job every 1–5 minutes (without webhooks this is the sync) using vacancies(changedSince: <last run>, sortBy: LAST_PUBLICATION_CHANGE_AT, sortOrder: ASC), plus a daily full import that takes offline anything missing from the list.
- Store every public id of a vacancy (the uuid field is a list, first entry is current). Old ids redirect to the current job URL.
3. Job pages. Sanitize descriptions[].text (HTML) with an allowlist. Show only location, zipCode and jobLocationAddress to visitors; use searchZipCode, geoLocation, simpleLocation(s) and mongoLocation for search and filtering only.
4. Tracking via POST /v1/track-action, server-side, fire and forget:
- VACANCY_VIEWED on every job-page view.
- VACANCY_APPLICATION_FORM_OPENED once per page view, on first interaction with the application form (browser notifies our backend, backend forwards).
- Always send webOrigin = the visitor's full browser URL with all utm_* parameters intact (persist the entry URL's UTM parameters across the session), referrer = document.referrer, and the visitor's user agent.
5. Application form posting to our backend. Run CAPTCHA or honeypot there, then forward as multipart/form-data to POST /v1/job-application with up to 5 files (10 MB in total), plus webOrigin and referrer. Required fields: firstName, lastName, sex, email.
6. Preview page at <https://our-site.ch/preview/:previewToken> using the previewVacancy(previewToken) query (no API key, may run in the browser) and the previewVacancyUpdates subscription for live refresh. Taloo embeds this page in an iframe, so allow framing from *.taloo.ch. noindex, not in listings, no tracking events.
Verify each step against the demo environment. Finish with the go-live checklist from the guide: tick what you verified, list what you couldn't.
1Authenticate Required
Send the key as x-channel-api-key on every request: GraphQL, REST and the GraphQL WebSocket. No logins, no token refresh.
curl -H "x-channel-api-key: <key>" -H "Content-Type: application/json" \
-d '{"query":"{ vacancies(first: 1) { totalCount } }"}' https://1764.qa.api.taloo.ch/v1/graphql
totalCount equals the number of vacancies published on the channel.2Sync vacancies Required
Three mechanisms. Import and periodic sync are required; webhooks are optional and bring changes to your site within seconds instead of minutes.
a) Initial import Required
query Vacancies($after: String) {
vacancies(first: 100, after: $after) {
pageInfo { hasNextPage endCursor }
edges { node {
uuid # list; first entry is the current public id
title language jobPostingDate
location zipCode jobLocationAddress { street zip location }
workloadText employmentType salaryText
descriptions { type title text }
contactPerson { firstName lastName email phone }
contactCompany { name }
searchZipCode geoLocation { lat lon }
isco19chOccupations { code label }
} }
}
}
Repeat with after: endCursor while hasNextPage. Max page size 100. All fields: Playground.
b) Webhooks Optional
Taloo POSTs JSON to your publish and unpublish URLs:
// new or updated: fetch vacancy(publicUuid) and upsert
{ "event": "publish", "channelUuid": "…", "publicVacancyUuid": "…" }
// take offline
{ "event": "unpublish", "channelUuid": "…", "publicVacancyUuid": "…" }
Respond 2xx quickly and do the work afterwards. Failures are retried 3 times over a few minutes.
Authorization: Bearer …); we send it with every call, you reject calls without it.c) Periodic sync Required
vacancies(first: 100, changedSince: "<start of previous run>",
sortBy: LAST_PUBLICATION_CHANGE_AT, sortOrder: ASC)
With webhooks, run it every few minutes to catch what a webhook missed. Without webhooks it is your sync: run it every 1–5 minutes. changedSince only returns vacancies that are still live, so once a day do a full import and take offline whatever is missing from it.
Public ids
uuid is a list; the first entry is the current public id. Use it in your job URL. A republish creates a new id: you get unpublish for the old and publish for the new. vacancy(publicUuid) still resolves old ids, so redirect old URLs instead of returning 404.
Rendering
descriptions[].textis HTML. Sanitize with an allowlist.- Recruiters can hide the exact address. Show
location,zipCode,jobLocationAddress(empty when hidden). UsesearchZipCode,geoLocation,simpleLocation(s),mongoLocationfor search and filters only, never on the page. They are always filled. - Job categories: derive them from
isco19chOccupations, a relevance-ordered list of{ code, label }from the Swiss CH-ISCO-19 nomenclature (first entry is the best match). Codes are hierarchical; the first digit is the major group. Keep your own mapping from groups to categories, built from the public catalogue at i14y.admin.ch.occupationandindustriesare legacy and no longer filled.
3Track views and form opens Required
Two events, sent from your backend to POST /v1/track-action:
VACANCY_VIEWED | Every job-page view |
VACANCY_APPLICATION_FORM_OPENED | Once per page view, on first interaction with the form (e.g. focus) |
POST https://1764.qa.api.taloo.ch/v1/track-action
{
"action": "VACANCY_VIEWED",
"vacancyPublicUuid": "<public id>",
"webOrigin": "https://your-site.ch/jobs/abc?utm_source=linkedin&utm_medium=social",
"referrer": "https://www.linkedin.com/",
"userAgent": "Mozilla/5.0 …"
}
webOrigin: the visitor's full browser URL with allutm_*parameters intact. This is how Taloo attributes the visit to LinkedIn, a newsletter or your own site. If visitors land on a list page first, keep the entry URL's UTM parameters (e.g. in the session) and send those.referrer:document.referrer.userAgent: the visitor's, not your server's; used for bot filtering.- Fire and forget; never let it delay the page. Browser-side events (form focus) call your backend, which forwards them.
Submitted applications are counted by step 4; no extra event needed.
?utm_source=test-guide and focus the form. Ask us to confirm both events arrived with source test-guide.4Accept applications Required
Your form posts to your backend; your backend runs spam protection (CAPTCHA, honeypot) and forwards to POST /v1/job-application as multipart/form-data. Taloo trusts what your server sends.
curl -X POST https://1764.qa.api.taloo.ch/v1/job-application -H "x-channel-api-key: <key>" \
-F firstName=Anna -F lastName=Muster -F sex=FEMALE -F email=anna@example.com \
-F phone="+41 79 123 45 67" -F language=DE \
-F vacancyPublicUuid=<public id> \
-F webOrigin="https://your-site.ch/jobs/abc?utm_source=linkedin" \
-F referrer="https://www.linkedin.com/" \
-F files=@cv.pdf -F files=@letter.pdf
- Required:
firstName,lastName,sex,email. Up to 5 files, 10 MB in total per application. webOriginandreferrer: same rules as step 3.- Spontaneous application: omit
vacancyPublicUuid. - Response
{ "success": true, "applicationId": "…" }; processing is asynchronous.
5Preview drafts Recommended
Before publishing, the recruiter looks at the draft inside Taloo: Taloo embeds your preview URL, with the draft's previewToken filled in, in a frame on the vacancy, so the recruiter sees the job in your layout. The token is the credential, so your preview page runs without an API key, in the browser if you like. Edits in Taloo arrive through the subscription, so the frame updates while the recruiter types.
query { previewVacancy(previewToken: "…") { title location descriptions { type title text } } }
# optional live refresh: emits on every edit, then re-run the query
subscription { previewVacancyUpdates(previewToken: "…") }
Mark preview pages noindex, keep them out of listings, and don't send tracking events for them. The page must allow framing by Taloo: no X-Frame-Options: DENY, and a frame-ancestors that includes *.taloo.ch if you set one.
Stage 2 · Your channel settings
Send us these and we create your channel, first on demo, then on production. Placeholders are replaced by Taloo when building links. On demo your channel is part of the seed, so it survives every reset with the same key and settings.
| Setting | Example | Used for |
|---|---|---|
| Platform name Required | Your Platform | Channel name recruiters see in Taloo |
| Website URL Required | https://your-site.ch | Link to your platform in Taloo |
| Icon URL Required | https://your-site.ch/favicon.svg | Small square icon (favicon-size, SVG or PNG) next to the channel name |
| Logo URL Recommended | https://your-site.ch/logo.svg | Full-size logo in larger views |
| Job page URL Required | https://your-site.ch/jobs/:publicUuid | Recruiters open and share the published job |
| Apply URL Required | https://your-site.ch/jobs/:publicUuid#apply | Direct "apply" link; recruiters and other platforms use it for an apply button. A separate page is not needed: your job page URL with a fragment or query that opens the form is fine |
| Preview URL Recommended | https://your-site.ch/preview/:previewToken | Step 5, if you build the preview |
| Webhook URLs Optional | https://your-site.ch/api/taloo/webhook | Step 2b, if you use webhooks. One URL for publish and unpublish, or two |
| Webhook secret header With webhooks | Authorization: Bearer … | Sent with every webhook call; required as soon as you give us webhook URLs |
| Technical contact Required | dev@your-site.ch | Who we reach when webhooks fail or the key rotates |
All URLs must be publicly reachable over HTTPS. The icon and logo are loaded directly by recruiters' browsers.
You get back
- Your channel's API key for demo, later for production
- Your
channelUuid, as it appears in webhook payloads. Events and applications that carry noutm_sourceof their own are attributed to your channel automatically; nothing to configure - Only if you run several sites on one channel: a UTM source slug (e.g.
your-platform) that names your platform in the recruiter's statistics instead of the uuid. Tell us if that is you
Go-live checklist
- ☐ API key only used server-side
- ☐ Import and periodic sync work (and webhooks, if used); unpublished vacancies disappear; old ids redirect
- ☐ Job page and apply URLs open the right job and its form
- ☐ Visitors see display location fields only; description HTML sanitized
- ☐ Both tracking events arrive with UTM parameters intact
- ☐ Test application with files arrives; form has spam protection
- ☐ Preview opens from Taloo
Once this passes on demo, we hand you the production key and switch the channel over.