Developers
Connect QuikPitch to your own systems
Put video applications and your company profile on your own website, pull your jobs, applications and company details into your own software, and get told the moment something happens. You manage all of it under Integrations in your dashboard.
Embed on your website
Show a job's application form, a list of your open jobs, or your company profile card inside your own site. Candidates apply by video without leaving it, and need no account: they confirm their email with a code, then send.
1. Allow your website
In Integrations, add the sites that will show the form (yourcompany.com also covers www; use *.yourcompany.com for every subdomain). Browsers will refuse to show it anywhere else, which also stops someone placing your form on a look-alike site.
2. Paste the snippet
<script src="https://quikpitch.co/embed.js" async></script> <!-- one job's application form --> <div data-quikpitch data-job="JOB_ID"></div> <!-- or a list of all your open jobs --> <div data-quikpitch data-company="your-username"></div> <!-- or your company profile card --> <div data-quikpitch data-profile="your-username"></div>
Options, set on the div: data-height (auto makes the frame grow and shrink with the form, filling the screen while the camera or a pop-up is open; or a fixed height like 800px, the default, which scrolls inside the frame) and data-accent (a hex colour such as #2563eb for buttons, overriding the colour saved in Integrations). Only public jobs can be embedded.
The profile card (data-profile) shows what's on your QuikPitch company page: your logo, name and tagline, about us, how you hire, benefits, company details and your intro video if you've made one. It follows your edits within a few minutes, so there's nothing to keep in sync. It appears on the websites you've listed under Integrations whatever your page's own visibility is set to; it needs your profile to be set up (name and location).
In Integrations you can also style the form's text: the title at the top (hidden, or small, medium or large), the section headings and the body text each get their own font (QuikPitch's own, or your visitors' system sans-serif or serif) and size, so it sits naturally on your site.
Knowing when someone applies
document.querySelector("[data-quikpitch]")
.addEventListener("quikpitch:applied", (event) => {
console.log(event.detail); // { source: "quikpitch", type: "applied", jobId, applicationId }
});The event carries no personal details. To get the application itself, use the API or a webhook below.
Good to know
- The page must be served over https for the camera to work.
- Some in-app browsers (LinkedIn, Instagram, Facebook) don't allow camera use in a frame. The form detects this and offers to open the same application in the person's browser.
- A “Powered by QuikPitch” line appears under the form unless your plan lets you hide it.
The API
A JSON API for reading your jobs and applications, shortlisting applications, keeping shortlists of candidates, and reading and updating your company profile. Make a key under Integrations (it's shown once; store it like a password and never put it in a web page), then send it on every request:
curl https://quikpitch.co/api/v1/jobs \ -H "Authorization: Bearer qp_live_..."
- A key only ever sees your own jobs and applications. Revoking it stops it working immediately.
- Limit, per key, by plan: 120 requests a minute on Starter, 300 on Growth and 1200 on Enterprise. Every response has
X-RateLimit-Limit,X-RateLimit-RemainingandX-RateLimit-Reset(seconds); beyond it you get429withRetry-After. - Errors look like
{"error":{"code":"not_found","message":"..."}}with a matching HTTP status. - Lists are newest first and paged: pass
limit(1–100, default 50) and the previousnext_cursorascursor.next_cursorisnullon the last page.
GET /api/v1/jobs
Your jobs, with how many applications each has at every stage. Filter with status (live, paused, filled, archived).
{
"data": [{
"id": "17ba5f4f-...", "title": "Senior Developer", "location": "Inverness, UK",
"contract_type": "Full-time", "salary_range": "£50k - £70k",
"status": "live", "visibility": "public",
"created_at": "2026-10-01T23:50:00.000Z", "url": "https://quikpitch.co/jobs/17ba5f4f-...",
"application_counts": { "total": 12, "new": 5, "shortlisted": 3, "interview": 2, "rejected": 2 }
}],
"next_cursor": null
}GET /api/v1/jobs/{id}
One job in full: description, benefits, video_questions and extra_fields.
GET /api/v1/company
Your company profile, as candidates see it on your page, plus two numbers only you can see: how many candidates follow you (a count, never who) and how many jobs you have open.
{
"username": "acme-coffee",
"name": "Acme Coffee Roasters",
"tagline": "Small-batch roasters, big on people",
"about": "We roast coffee in Bristol...",
"how_we_hire": "Two short videos, then a chat with the team.",
"perks": ["Free lunch", "25 days holiday"],
"industry": "Hospitality", "size": "11-50", "founded": 2014,
"location": "Bristol, UK", "address": "12 Wapping Wharf, Bristol BS1 6UD",
"website": "https://acmecoffee.co.uk",
"linkedin": "https://www.linkedin.com/company/acme-coffee",
"profile_visibility": "public",
"profile_url": "https://quikpitch.co/company/acme-coffee",
"logo_url": "https://...", "cover_image_url": null,
"intro_video": { "playback_url": "https://quikpitch.co/api/media/...", "thumbnail_url": "https://..." },
"followers": 42, "open_jobs": 3
}PATCH /api/v1/company
Change your profile from your own systems (an HR tool, your CMS). Send only what you want to change; the whole updated profile comes back. Your public page updates straight away.
curl -X PATCH https://quikpitch.co/api/v1/company \
-H "Authorization: Bearer qp_live_..." -H "Content-Type: application/json" \
-d '{"tagline":"Roasting since 2014","perks":["Free lunch","Hybrid working"],"founded":2014}'You can change name, tagline, about (up to 2,000 characters), how_we_hire (800), perks (a list of up to 12), industry, size (1-10, 11-50, 51-200, 201-500, 501-1,000, 1,000+ or ""), founded (a four-digit year, or null), address, website, linkedin (your company page) and profile_visibility (public, platform for signed-in candidates only, or hidden for no page). Anything else, or a value that breaks a rule, is a 400 that says which. The logo, cover image, intro video and location are set in your dashboard: each needs an upload or the place picker.
GET /api/v1/applications
Applications to your jobs. Filter with job_id, status (new, shortlisted, interview, rejected) and created_after (an ISO date: ask only for what's new since you last looked).
{
"data": [{
"id": "5e3e3b57-...", "job_id": "17ba5f4f-...", "job_title": "Senior Developer",
"status": "new",
"review_url": "https://quikpitch.co/dashboard/jobs/17ba5f4f-.../review?a=5e3e3b57-...", // opens it in QuikPitch (team sign-in)
"candidate": {
"name": "Sam Jones", "job_title": "Developer", "location": "Inverness",
"username": "sam-jones", // set for QuikPitch members: use it with shortlists
"profile_url": null, // set for QuikPitch members
"email": "sam@example.com", // set for guests, and for members who chose to share it
"phone": null
},
"source": "embed", // platform, embed, or invite (answered an invitation to pitch)
"external_ref": null, // your own reference, for someone you invited (not yet sent via the API)
"applied_at": "2026-10-07T16:12:04.416Z",
"updated_at": "...", "viewed_at": null
}],
"next_cursor": "MTc5..."
}Privacy: email and phone are included for people who applied without an account (they gave them to apply). For QuikPitch members, email is set only if they ticked “share my email” on the application (and it’s removed if they withdraw it); phone is never included. Otherwise you get their name and public profile link: message them from your dashboard.
GET /api/v1/applications/{id}
One application in full: availability, salary expectation, cover message, the pitch and each answers[] video, extra_fields, and any rejection or interview. A video is { "playback_url", "thumbnail_url" }: the playback URL stays valid and sends the player to a fresh, short-lived link each time.
PATCH /api/v1/applications/{id}
Shortlist an application, or move it back:
curl -X PATCH https://quikpitch.co/api/v1/applications/5e3e3b57-... \
-H "Authorization: Bearer qp_live_..." -H "Content-Type: application/json" \
-d '{"status":"shortlisted"}'Only new and shortlisted are accepted. Rejecting and inviting to interview each send a message to the candidate, so they're done in your dashboard; a rejected or interviewing application can't be moved this way (409).
Shortlists
Your lists of candidates, the same ones as under Shortlists in your dashboard (a list can be tied to one of your jobs). Candidates are named by their public username, which an application gives you as candidate.username. Only candidates who are live (they've recorded a pitch) and haven't paused their profile can be added or are shown.
GET /api/v1/shortlists: your lists, newest first, with how many candidates each holds.POST /api/v1/shortlistswith{ "name": "Front-end shortlist", "job_id": "...", "candidates": ["sam-jones"] }: make one (job_idandcandidatesare optional).GET /api/v1/shortlists/{id}: one list with its candidates. Each has aname,job_title,location,profile_url(their QuikPitch profile),availabilityand theirpitch(playback_urlandthumbnail_url).PATCH /api/v1/shortlists/{id}withnameand/orjob_id(nullunlinks).DELETE /api/v1/shortlists/{id}: delete the list. The candidates themselves are untouched.PUT /api/v1/shortlists/{id}/candidates/{username}adds a candidate andDELETEon the same address takes them off. Both are safe to repeat and return the updated list.
Invitations to complete a video application
Invite people you already have (from your applicant system, say) to complete the video application for a job: a short video answer to each of its questions, plus an optional 30-second introduction. Each person gets an email from QuikPitch with a private link: no account needed. They aren't added to QuikPitch as a candidate: they only become an application when they send it, and an invitation that is never answered ends after 30 days and is deleted. Only invite people you have a reason to contact about the job.
POST /api/v1/jobs/{id}/inviteswith{ "people": [{ "email": "sam@example.com", "name": "Sam Jones", "external_ref": "ats-4821" }], "note": "Great to meet you", "send_email": true }: up to 50 at a time.nameandexternal_refare optional;external_refis your own reference and comes back asexternal_refon the application and its webhooks. The job must be open. Each person gets aresult:sent,already_invited,already_applied,said_no,suppressed(asked not to be emailed),invalid,limit(daily limit) ortrial_limit(a free trial includes 10 invitations in all; a paid plan, 100 a day on Starter, 300 on Growth and 1,000 on Enterprise).note(optional) is a short plain-text message, up to 300 characters with no links or email addresses, shown in the invitation email as a note from your company. Write{first_name}where you want each person's own first name.send_email(defaulttrue): set it tofalseand QuikPitch sends nothing: each invitation comes back with aninvite_url(the person's private link) and aninvite_token, for you to send yourself, for example from your own email or applicant system. There are no reminders for these. Using the embed on your own site? Send them your page's address with?qp_invite={invite_token}on the end: the form in the frame then knows who they are, and their application is linked to the invitation.GET /api/v1/jobs/{id}/invites(optionally?status=pending): a job's invitations, newest first.GET /api/v1/invites/{id}: one invitation:status(pending,applied,declined,expiredorcancelled) and, once they pitch, theapplication_id.DELETE /api/v1/invites/{id}: withdraw an open invitation: its link stops working.
Webhooks
Rather than asking the API again and again, add an endpoint under Integrations and we'll POST an event to it as things happen. The address must be https:// and reachable on the public internet.
| application.created | A new application arrives |
| application.status_changed | An application is shortlisted or moved back |
| application.rejected | An application is rejected |
| interview.invited | A candidate is invited to interview |
| interview.accepted | An interview time is confirmed |
| interview.declined | A candidate declines an interview |
| interview.times_requested | A candidate asks for different times |
| interview.cancelled | An interview is cancelled |
| invite.declined | Someone you invited says no thanks |
| invite.expired | An invitation runs out without a pitch |
| job.created | A job is posted |
| job.closed | A job is closed or marked filled |
| job.expired | A job closes itself at the end of its run |
| job.reopened | A closed job is reopened or renewed |
What we send
POST https://yourcompany.com/quikpitch-webhook
Content-Type: application/json
X-QuikPitch-Event: application.status_changed
X-QuikPitch-Delivery: 0b3f...
X-QuikPitch-Signature: t=1791389524,v1=9f2c...
{
"id": "0b3f...",
"type": "application.status_changed",
"created_at": "2026-10-07T16:12:04.416Z",
"data": {
"application": { "id": "5e3e...", "job_id": "17ba...", "status": "shortlisted", "candidate": { ... } },
"previous_status": "new",
"api_url": "https://quikpitch.co/api/v1/applications/5e3e..."
}
}Events are small on purpose: fetch api_url for everything else (including the videos). Interview events also carry an interview object; application.rejected carries the message you sent.
Subscribe from an app (POST /api/v1/webhooks)
Tools such as Zapier subscribe and unsubscribe for you, using your API key, so you don't add an endpoint by hand. (Setting up Zapier itself is on the Integrations page.) POST /api/v1/webhooks with { "url": "https://...", "events": ["application.created"] } returns an id; DELETE /api/v1/webhooks/{id} removes it. These are delivered and signed exactly like the ones above, have their own allowance (up to 100), and only the ones made this way can be removed this way.
Job events
job.created, job.closed, job.expired and job.reopened carry the job as the API lists it, and where to fetch it in full. job.closed says how it closed in reason (archived or filled); job.expired is a job closing itself at the end of its run (reason is expired), so you can tell your own closing from the clock's. Renewing or reopening a closed job sends job.reopened.
{
"id": "7c1d...",
"type": "job.expired",
"created_at": "2026-10-09T04:30:12.118Z",
"data": {
"job": {
"id": "17ba5f4f-...", "title": "Senior Developer", "location": "Inverness, UK",
"contract_type": "Full-time", "salary_range": "£50k - £70k",
"status": "archived", "visibility": "public",
"created_at": "2026-08-10T09:00:00.000Z", "url": "https://quikpitch.co/jobs/17ba5f4f-..."
},
"reason": "expired",
"api_url": "https://quikpitch.co/api/v1/jobs/17ba5f4f-..."
}
}Check the signature
Every delivery is signed with your endpoint's secret (shown once, when you add it): an HMAC-SHA256 of {timestamp}.{raw body}. Verify it on the raw body, and reject old timestamps.
import crypto from "node:crypto";
function verify(rawBody, header, secret, toleranceSeconds = 300) {
const parts = Object.fromEntries(header.split(",").map((p) => p.split("=")));
if (Math.abs(Date.now() / 1000 - Number(parts.t)) > toleranceSeconds) return false;
const expected = crypto.createHmac("sha256", secret).update(`${parts.t}.${rawBody}`).digest("hex");
const given = Buffer.from(parts.v1 ?? "", "utf8");
const wanted = Buffer.from(expected, "utf8");
return given.length === wanted.length && crypto.timingSafeEqual(given, wanted);
}Delivery and retries
- Answer with any
2xxwithin 8 seconds to say you got it. - A timeout, network error,
5xxor429is retried twice more, a few seconds apart. Other4xxanswers aren't retried. - Events can occasionally arrive twice, or out of order: use the
idto ignore repeats and the timestamps to order them. - An endpoint that fails 20 times in a row is switched off (you can switch it back on).
- The last month of deliveries is in the endpoint's Log, and Test sends a sample event.

