openapi: 3.2.0
info:
contact:
email: support@aiapplyd.com
name: AI Applyd
url: https://aiapplyd.com
description: 'The AI Applyd API runs the whole job hunt for you: it matches you to roles that fit your profile, rewrites your resume for each posting, writes the cover letter the form asks for, and auto-applies across the major applicant tracking systems.'
license:
name: Proprietary
url: https://aiapplyd.com/legal/terms
title: AI Applyd Outreach API
version: 115c47ce
servers:
- description: Current environment
url: https://api.aiapplyd.com/api/v1
security:
- BearerAuth: []
tags:
- name: outreach
paths:
/outreach/job/{jobId}/contacts:
get:
tags:
- outreach
summary: Get Outreach Contacts For A Saved Job
description: List sourced hiring contacts for a SAVED job's company, before any application exists. Outreach is generated server-side at save time, so this is normally a cache read. Same payload as the application-scoped endpoint, including the addresses the employer printed in the job description itself.
parameters:
- schema:
type: integer
description: job_listing row id
example: 4321
exclusiveMinimum: 0
required: true
description: job_listing row id
name: jobId
in: path
responses:
'200':
description: Contacts retrieved (cached or freshly sourced)
content:
application/json:
schema:
$ref: '#/components/schemas/OutreachContactsResponse'
'401':
description: Unauthorized
content:
application/json:
schema:
$ref: '#/components/schemas/ApiErrorResponse'
'404':
description: Not Found
content:
application/json:
schema:
$ref: '#/components/schemas/ApiErrorResponse'
'500':
description: Internal Server Error
content:
application/json:
schema:
$ref: '#/components/schemas/ApiErrorResponse'
operationId: getOutreachJobByJobIdContacts
/outreach/job/{jobId}/draft:
post:
tags:
- outreach
summary: Draft Outreach Message For A Saved Job
description: Generate a personalized outreach DM/email draft for one sourced contact on a SAVED job, before any application exists. Drafts only; the user sends from their own account.
parameters:
- schema:
type: integer
description: job_listing row id
example: 4321
exclusiveMinimum: 0
required: true
description: job_listing row id
name: jobId
in: path
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/OutreachDraftBody'
responses:
'200':
description: Draft generated
content:
application/json:
schema:
$ref: '#/components/schemas/OutreachDraftResponse'
'400':
description: Bad Request
content:
application/json:
schema:
$ref: '#/components/schemas/ApiErrorResponse'
'401':
description: Unauthorized
content:
application/json:
schema:
$ref: '#/components/schemas/ApiErrorResponse'
'404':
description: Not Found
content:
application/json:
schema:
$ref: '#/components/schemas/ApiErrorResponse'
'500':
description: Internal Server Error
content:
application/json:
schema:
$ref: '#/components/schemas/ApiErrorResponse'
operationId: postOutreachJobByJobIdDraft
/outreach/{applicationResultId}/contacts:
get:
tags:
- outreach
summary: Get Outreach Contacts
description: List sourced hiring contacts for the application's company. Returns cached rows when present; otherwise triggers a cheap sourcing run (team page -> Google SERP -> derived email), rate-limited to one run per company per 24h. Always returns a LinkedIn People-tab deep-link so the user is never at a dead end.
parameters:
- schema:
type: integer
description: Application result row id
example: 1234
exclusiveMinimum: 0
required: true
description: Application result row id
name: applicationResultId
in: path
responses:
'200':
description: Contacts retrieved (cached or freshly sourced)
content:
application/json:
schema:
$ref: '#/components/schemas/OutreachContactsResponse'
'401':
description: Unauthorized
content:
application/json:
schema:
$ref: '#/components/schemas/ApiErrorResponse'
'404':
description: Not Found
content:
application/json:
schema:
$ref: '#/components/schemas/ApiErrorResponse'
'500':
description: Internal Server Error
content:
application/json:
schema:
$ref: '#/components/schemas/ApiErrorResponse'
operationId: getOutreachByApplicationResultIdContacts
/outreach/{applicationResultId}/draft:
post:
tags:
- outreach
summary: Draft Outreach Message
description: Generate a personalized outreach DM/email draft for one sourced contact. Reuses the follow-up draft generator (auto channel detection + anti-cliché filter). Returns the draft, channel, the contact's LinkedIn URL, and a pre-filled mailto link. Drafts only; the user sends from their own account.
parameters:
- schema:
type: integer
description: Application result row id
example: 1234
exclusiveMinimum: 0
required: true
description: Application result row id
name: applicationResultId
in: path
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/OutreachDraftBody'
responses:
'200':
description: Draft generated
content:
application/json:
schema:
$ref: '#/components/schemas/OutreachDraftResponse'
'400':
description: Bad Request
content:
application/json:
schema:
$ref: '#/components/schemas/ApiErrorResponse'
'401':
description: Unauthorized
content:
application/json:
schema:
$ref: '#/components/schemas/ApiErrorResponse'
'404':
description: Not Found
content:
application/json:
schema:
$ref: '#/components/schemas/ApiErrorResponse'
'500':
description: Internal Server Error
content:
application/json:
schema:
$ref: '#/components/schemas/ApiErrorResponse'
operationId: postOutreachByApplicationResultIdDraft
components:
schemas:
OutreachContact:
type: object
properties:
checkedAt:
type:
- string
- 'null'
description: ISO timestamp of when the public source for this person was last checked. Drives `stillThere`.
example: '2026-09-05T10:00:00.000Z'
confidence:
type:
- string
- 'null'
enum:
- low
- medium
- high
- null
description: Confidence in this contact and its details
example: low
drafts:
$ref: '#/components/schemas/OutreachContactDrafts'
email:
type:
- string
- 'null'
description: 'Email address, or null when we hold none. An address may still ship with `emailStatus: unknown` - that means we hold it but cannot cite the page it came from.'
example: jane.doe@acme.com
emailEvidenceUrl:
type:
- string
- 'null'
description: 'verified: the public page the address appears on. pattern: the page the sample address was observed on. unknown: null.'
example: https://acme.com/team
emailPatternNote:
type:
- string
- 'null'
description: 'For `pattern` emails only: the pattern used and the real address it was observed on, so the user can judge the guess.'
example: first.last, seen on john.smith@acme.com
emailSource:
type:
- string
- 'null'
enum:
- scraped
- derived
- verified
- null
description: 'Provenance of the email: scraped (read off a page) or derived (built from an observed pattern). ALWAYS null when `email` is null, so a provenance chip is never rendered beside an absent address.'
example: derived
emailStatus:
type: string
enum:
- verified
- pattern
- unknown
description: 'verified: appears on a public page we fetched AND `emailEvidenceUrl` names that page. pattern: inferred from a pattern observed on a real address at this domain. unknown: either no address at all, or an address we hold but cannot cite a page for - verified is never claimed without the page.'
example: verified
evidence:
type: string
description: 'One-sentence statement of what was actually matched for this person (name-to-profile, company-in-search-result, title-in-result). Never empty: a contact with no evidence is never surfaced.'
example: Their name matches this LinkedIn profile, and the public search result lists them as Technical Recruiter at Acme.
fullName:
type: string
description: Full name of the contact
example: Jane Doe
id:
type: integer
description: company_contact row id
example: 9001
exclusiveMinimum: 0
evidenceUrl:
type:
- string
- 'null'
description: The public page the evidence sentence was derived from (the company team page, or the search-results page). Null for rows sourced before this field existed.
example: https://acme.com/team
linkedinUrl:
type:
- string
- 'null'
description: Public LinkedIn profile URL (deep-link only)
example: https://www.linkedin.com/in/jane-doe
rank:
type: integer
description: Position in the relevance ranking for the applied role (1 = best). Recruiters, hiring managers and team leads rank first.
example: 1
exclusiveMinimum: 0
seniority:
type:
- string
- 'null'
enum:
- recruiter
- hiring_manager
- lead
- exec
- null
description: Coarse seniority bucket
example: recruiter
sourcedAt:
type:
- string
- 'null'
description: ISO timestamp of when this contact was last sourced/refreshed from public pages. Outreach data ages - people change roles - so the UI surfaces this instead of asserting the row is current.
example: '2026-07-30T10:00:00.000Z'
source:
type:
- string
- 'null'
enum:
- team_page
- google_serp
- null
description: Where the contact was sourced (team_page | google_serp)
example: team_page
stillThere:
type: string
enum:
- likely
- possible
- stale
description: 'From the evidence date alone: likely (checked within 90 days), possible (91-180 days), stale (older than 180 days, verify before sending).'
example: likely
title:
type:
- string
- 'null'
description: Job title / role
example: Senior Technical Recruiter
required:
- checkedAt
- confidence
- drafts
- email
- emailEvidenceUrl
- emailPatternNote
- emailSource
- emailStatus
- evidence
- fullName
- id
- evidenceUrl
- linkedinUrl
- rank
- seniority
- sourcedAt
- source
- stillThere
- title
additionalProperties: false
OutreachContactsResponse:
type: object
properties:
data:
$ref: '#/components/schemas/OutreachContactsData'
meta:
$ref: '#/components/schemas/ResponseMetadata'
success:
type: boolean
enum:
- true
required:
- data
- success
additionalProperties: false
description: Successful API response
OutreachDraftBody:
type: object
properties:
contactId:
type: integer
description: company_contact row id to draft a message for
example: 9001
exclusiveMinimum: 0
required:
- contactId
additionalProperties: false
OutreachDraftResponse:
type: object
properties:
data:
$ref: '#/components/schemas/OutreachDraftData'
meta:
$ref: '#/components/schemas/ResponseMetadata'
success:
type: boolean
enum:
- true
required:
- data
- success
additionalProperties: false
description: Successful API response
ResponseMetadata:
type: object
properties:
duration:
type: number
description: Request duration in milliseconds
example: 150
exclusiveMinimum: 0
performance:
type: object
properties:
dbQueries:
type: integer
minimum: 0
totalDbTime:
type: number
minimum: 0
description: Performance metrics (preview/local only)
requestId:
type: string
description: Unique request identifier
example: req_abc123
timestamp:
type: string
format: date-time
description: ISO 8601 timestamp
example: '2026-09-25T17:29:47.224Z'
version:
type: string
description: API version
example: v1
OutreachDraftData:
type: object
properties:
alternates:
type: array
items:
$ref: '#/components/schemas/OutreachDraftAlternate'
description: The SAME message written for every other channel, generated in the same request and grounded in the same evidence. A cold outreach is not one route - the user can paste into LinkedIn or into an email client without regenerating.
body:
type: string
description: Drafted outreach message. For LinkedIn channel, capped at 300 chars.
example: Hi Jane, I just applied for the Senior Backend Engineer role...
channel:
type: string
enum:
- email
- linkedin
- other
description: Auto-detected channel (email when an address is known, else linkedin)
example: email
linkedinUrl:
type:
- string
- 'null'
description: LinkedIn URL of the contact, for the "Open on LinkedIn" deep-link
example: https://www.linkedin.com/in/jane-doe
mailto:
type:
- string
- 'null'
description: 'Pre-filled mailto: link when the contact has an email, else null'
example: mailto:jane.doe@acme.com?subject=...&body=...
subject:
type: string
description: Email subject line. Empty when channel=linkedin.
example: Following up on my application
required:
- alternates
- body
- channel
- linkedinUrl
- mailto
- subject
additionalProperties: false
OutreachLinkedinNote:
type: object
properties:
body:
type: string
maxLength: 300
description: LinkedIn connection note, 300 characters or fewer (LinkedIn's limit).
required:
- body
additionalProperties: false
OutreachContactDrafts:
type: object
properties:
email:
$ref: '#/components/schemas/OutreachEmailDraft'
linkedin:
$ref: '#/components/schemas/OutreachLinkedinNote'
required:
- email
- linkedin
additionalProperties: false
description: 'Deterministic drafts for this person: an email (subject + body) and a LinkedIn connection note. Nothing here is sent by us.'
OutreachCompany:
type:
- object
- 'null'
properties:
checkedAt:
type:
- string
- 'null'
description: ISO timestamp of when the company's own site was last fetched for this block. Null when nothing was fetched yet.
example: '2026-09-05T10:00:00.000Z'
domain:
type:
- string
- 'null'
description: Company web domain on file (no scheme, no www).
example: acme.com
evidenceUrl:
type:
- string
- 'null'
description: The page `hq`, `socials` and the site-published industry tags were read from. Null when nothing was fetched (the industry on our own record may still appear in `industryTags`).
example: https://acme.com/
hq:
type:
- string
- 'null'
description: Headquarters as the company states it in its own structured data (schema.org Organization address). Null when not published.
example: Berlin, Germany
industryTags:
type: array
items:
type: string
description: 'Up to 6 industry / category tags, deduped. Two origins, and only two: what the company states about itself in its own structured data (schema.org `industry` / `knowsAbout`, read from `evidenceUrl`), and the industry on OUR record for this company, which is not page evidence and may be absent from the site. SEO `` is deliberately excluded - it is a crawler keyword dump, not a category list. Empty when neither origin has anything.'
example:
- Software
- Fintech
name:
type: string
description: Company name as it appears on the job
example: Acme
socials:
type: array
items:
$ref: '#/components/schemas/OutreachSocialHandle'
description: Social handles linked from the company's own homepage / footer, one per platform, each with the page it was found on.
website:
type:
- string
- 'null'
description: Homepage URL. Null when the company has no domain on file.
example: https://acme.com/
required:
- checkedAt
- domain
- evidenceUrl
- hq
- industryTags
- name
- socials
- website
additionalProperties: false
description: Company research block from the company's own site. Null when the job carries no company name.
OutreachSocialHandle:
type: object
properties:
evidenceUrl:
type: string
description: The company page the handle was linked from (homepage or footer).
example: https://acme.com/
platform:
type: string
enum:
- linkedin
- x
- instagram
- youtube
- discord
- github
description: Social platform of a handle found on the company's own website
example: linkedin
url:
type: string
description: The handle URL exactly as linked on the company's own site.
example: https://www.linkedin.com/company/acme
required:
- evidenceUrl
- platform
- url
additionalProperties: false
OutreachDraftAlternate:
type: object
properties:
body:
type: string
description: Draft body for this channel. LinkedIn variants are capped at 300 chars.
channel:
type: string
enum:
- email
- linkedin
- other
description: Channel this variant is written for
example: email
mailto:
type:
- string
- 'null'
description: 'Pre-filled mailto: link when this variant is the email channel and an address is known.'
subject:
type: string
description: Subject line. Empty for the LinkedIn variant.
required:
- body
- channel
- mailto
- subject
additionalProperties: false
OutreachContactsData:
type: object
properties:
company:
$ref: '#/components/schemas/OutreachCompany'
contacts:
type: array
items:
$ref: '#/components/schemas/OutreachContact'
description: The top 3 sourced contacts, ranked by relevance to the applied role (recruiter / hiring manager / team lead first). People whose evidence says they left are never included.
postingEmails:
type: array
items:
type: string
description: Contact addresses the employer printed in this job description. Extracted verbatim from the stored posting - no model, no inference, no network call - so they are the highest-provenance contact on this surface. Empty for most postings.
example:
- careers@acme.com
peopleDeepLink:
type: string
description: LinkedIn company People-tab deep-link so the user can keep searching themselves
example: https://www.linkedin.com/company/acme/people/?keywords=recruiter
rateLimited:
type: boolean
description: True when a fresh sourcing run was skipped (one run per company per 24h)
example: false
sourced:
type: boolean
description: True when this request triggered a fresh sourcing run
example: true
totalContacts:
type: integer
minimum: 0
description: How many verified contacts exist for this company before the top-3 cut.
example: 7
required:
- company
- contacts
- postingEmails
- peopleDeepLink
- rateLimited
- sourced
- totalContacts
additionalProperties: false
OutreachEmailDraft:
type: object
properties:
body:
type: string
description: Ready-to-paste email body, first person, no clichés (anti-cliché filter applied).
mailto:
type:
- string
- 'null'
description: 'Pre-filled mailto: link when the contact has an address, else null.'
subject:
type: string
description: Email subject line.
example: Applying for Senior Backend Engineer at Acme
required:
- body
- mailto
- subject
additionalProperties: false
ApiErrorResponse:
type: object
properties:
error:
type: object
properties:
code:
type: string
context:
oneOf:
- type: object
properties:
errorType:
type: string
enum:
- validation
field:
type: string
fieldErrors:
type: array
items:
type: object
properties:
code:
type: string
expected:
type: string
field:
type: string
message:
type: string
received:
type: string
required:
- field
- message
schemaName:
type: string
required:
- errorType
- type: object
properties:
attemptedEmail:
type: string
format: email
errorType:
type: string
enum:
- authentication
failureReason:
type: string
enum:
- invalid_credentials
- account_locked
- expired_token
- token_expired
- missing_token
- invalid_token
- session_expired
description: Reason for authentication failure
example: token_expired
ipAddress:
type: string
operation:
type: string
service:
type: string
userAgent:
type: string
required:
- errorType
- type: object
properties:
actualPermission:
type: string
errorType:
type: string
enum:
- authorization
requiredPermission:
type: string
resource:
type: string
resourceId:
type: string
userId:
type: string
required:
- errorType
- type: object
properties:
constraint:
type: string
errorType:
type: string
enum:
- database
operation:
type: string
enum:
- insert
- update
- delete
- select
- batch
- transaction
- migration
description: Type of database operation
example: select
resourceId:
type: string
sqlState:
type: string
table:
type: string
userId:
type: string
required:
- errorType
- type: object
properties:
endpoint:
type: string
errorType:
type: string
enum:
- external_service
httpStatus:
type: number
operation:
type: string
resourceId:
type: string
responseTime:
type: number
retryAttempt:
type: integer
service:
type: string
serviceName:
type: string
userId:
type: string
required:
- errorType
- type: object
properties:
errorType:
type: string
enum:
- resource
resource:
type: string
resourceId:
type: string
service:
type: string
userId:
type: string
required:
- errorType
- type: object
properties:
errorType:
type: string
enum:
- rate_limit
limit:
type: number
resetTime:
type: string
windowMs:
type: number
required:
- errorType
- type: object
properties:
configKey:
type: string
errorType:
type: string
enum:
- configuration
operation:
type: string
service:
type: string
required:
- errorType
description: Type-safe error context information
example:
errorType: validation
fieldErrors:
- field: email
message: Invalid email format
details:
oneOf:
- type: object
properties:
detailType:
type: string
enum:
- validation
validationErrors:
type: array
items:
type: object
properties:
code:
type: string
field:
type: string
message:
type: string
required:
- field
- message
required:
- detailType
- validationErrors
additionalProperties: false
- type: object
properties:
detailType:
type: string
enum:
- service_error
originalError:
type: string
serviceName:
type: string
required:
- detailType
- serviceName
additionalProperties: false
- type: object
properties:
currentSize:
type: number
detailType:
type: string
enum:
- batch
originalError:
type: string
statementCount:
type: number
required:
- detailType
additionalProperties: false
- type: object
properties:
detailType:
type: string
enum:
- rate_limit
limit:
type: number
resetTime:
type: string
windowMs:
type: number
required:
- detailType
- limit
- resetTime
- windowMs
additionalProperties: false
- type: object
properties:
conflictingField:
type: string
detailType:
type: string
enum:
- resource
resourceId:
anyOf:
- type: string
- type: number
resourceType:
type: string
required:
- detailType
additionalProperties: false
- type: object
properties:
detailType:
type: string
enum:
- generic
info:
type: string
required:
- detailType
additionalProperties: false
- type: object
properties:
currentTokens:
type: integer
minimum: 0
detailType:
type: string
enum:
- insufficient_tokens
requiredTokens:
type: integer
minimum: 0
required:
- currentTokens
- detailType
- requiredTokens
additionalProperties: false
message:
type: string
validation:
type: array
items:
type: object
properties:
code:
type: string
field:
type: string
message:
type: string
required:
- field
- message
additionalProperties: false
required:
- code
- message
additionalProperties: false
meta:
$ref: '#/components/schemas/ResponseMetadata'
success:
type: boolean
enum:
- false
required:
- error
- success
additionalProperties: false
securitySchemes:
BearerAuth:
description: 'Session token from signing in to AI Applyd, sent as `Authorization: Bearer `.'
scheme: bearer
type: http