| Method | Endpoint | Description |
|---|---|---|
| POST | /match-candidates | Unified candidate search with optional location and must-have filtering |
The Outreach API uses OAuth 2.0 for authentication, providing secure, auditable access with per-user accountability. All API requests require a valid access token.
Step 1: Authorization Request
Redirect users to our authorization endpoint to grant access:
GET https://outreach.taladria.com/api/oauth/authorize
?client_id=YOUR_CLIENT_ID
&redirect_uri=https://your-app.com/callback
&response_type=code
&state=RANDOM_STATE_STRING
Step 2: User Authorization
The user logs in with their Outreach account credentials. After approval, they are redirected back to
your redirect_uri with an authorization code.
Step 3: Token Exchange
Exchange the authorization code for access and refresh tokens:
POST https://outreach.taladria.com/api/oauth/token
Content-Type: application/x-www-form-urlencoded
grant_type=authorization_code
&code=AUTHORIZATION_CODE
&client_id=YOUR_CLIENT_ID
&client_secret=YOUR_CLIENT_SECRET
&redirect_uri=https://your-app.com/callback
Response:
{
"access_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6...",
"token_type": "Bearer",
"expires_in": 3600,
"refresh_token": "dGhpcyBpcyBhIHJlZnJlc2ggdG9rZW4..."
}
Include the access token in the Authorization header for all API requests:
Authorization: Bearer YOUR_ACCESS_TOKEN
| Token Type | Lifetime | Notes |
|---|---|---|
access_token |
1 hour | Use for API requests. Refresh when expired. |
refresh_token |
90 days | Use to obtain new access tokens. Store securely. |
POST https://outreach.taladria.com/api/oauth/token
Content-Type: application/x-www-form-urlencoded
grant_type=refresh_token
&refresh_token=YOUR_REFRESH_TOKEN
&client_id=YOUR_CLIENT_ID
&client_secret=YOUR_CLIENT_SECRET
Contact us at info@taladria.com to register your application and
receive your client_id and client_secret.
Never expose your client_secret in client-side code. Always make token exchanges from your
server. Store refresh tokens encrypted at rest.
Search your candidate database with a job description and get AI-ranked matches.
| Header | Value | Required |
|---|---|---|
Authorization |
Bearer YOUR_ACCESS_TOKEN | Yes |
Content-Type |
application/json | Yes |
{
"jobSpec": "Senior PHP Developer with Laravel experience, 5+ years, Dublin-based",
"topN": 10,
// Optional: Job title for improved search relevance and email subjects
"jobTitle": "Senior PHP Developer",
// Optional: Location filtering
"jobLocation": "Dublin, Ireland",
"radiusKm": 50,
// Optional: Must-have requirements (ALL must be met)
"mustHaves": "5+ years PHP, Laravel experience"
}
| Field | Type | Required | Description |
|---|---|---|---|
jobSpec |
string | Yes | Job description or requirements to match against |
topN |
integer | No | Number of candidates to return (1-100, default: 10) |
jobTitle |
string | No | Job title (e.g., "Senior PHP Developer"). Improves search relevance and used in email subjects. |
jobLocation |
string | No | Job location (e.g., "Dublin, Ireland") - will be geocoded automatically |
jobLatitude |
float | No | Job latitude (alternative to jobLocation if already geocoded) |
jobLongitude |
float | No | Job longitude (alternative to jobLocation if already geocoded) |
radiusKm |
integer | No | Search radius in km when using location (1-500, default: 50) |
mustHaves |
string | No | Comma-separated list of mandatory requirements. ALL must be met for a candidate to be returned. |
All filtering parameters are optional. You can use location filtering, must-haves filtering, both together, or neither for a basic semantic search.
{
"companyId": "your-company",
"matchCount": 5,
"totalScored": 10,
"clientRequirements": ["5+ years PHP", "Laravel experience", "Dublin-based"],
"promptInfo": {
"source": "company_specific",
"versionId": "match_scoring_20240105_0920",
"companyId": "your-company"
},
"matches": [
{
"resumeId": "abc123",
"name": "Alex Thompson",
"email": "alex@example.com",
"phone": "+353 87 1234567",
"location": "Dublin, Ireland",
"skills": ["PHP", "Laravel", "MySQL", "Docker", "Redis"],
"matchScore": 0.80,
"matchReasoning": "SCORE: 80\n\nSTRENGTHS:\n**Extensive PHP Experience:** 10 years of full-stack development...\n\nCONCERNS:\n**No AI/LLM Experience:** The role requires AI integration...\n\nRECOMMENDATION: YES - Strong technical foundation makes this candidate worth interviewing.\n\nINTERVIEW_FOCUS:\n**AI Learning Aptitude:** Assess willingness to learn AI/LLM technologies...",
"meets_all_must_haves": true,
"must_haves_identified": ["5+ years PHP", "Laravel experience", "Dublin-based"],
"bullhornId": "123456",
"bullhornUrl": "https://cls20.bullhornstaffing.com/BullhornStaffing/OpenWindow.cfm?Entity=Candidate&id=123456",
"atsType": "bullhorn"
}
]
}
| Field | Type | Description |
|---|---|---|
companyId |
string | Your company identifier |
matchCount |
integer | Number of qualified candidates (meeting all must-haves) |
totalScored |
integer | Total candidates scored by AI before must-have filtering |
clientRequirements |
array | Combined essential requirements identified from both JD and client input |
promptInfo |
object | Metadata about the AI prompt used (source, version, etc.) for transparency |
matches |
array | Array of candidate objects, ranked by match score (only those meeting all must-haves) |
matches[].resumeId |
string | Unique candidate identifier |
matches[].name |
string | Candidate's full name |
matches[].email |
string | Candidate's email address |
matches[].phone |
string | Candidate's phone number |
matches[].location |
string | Candidate's location |
matches[].skills |
array | List of skills extracted from CV |
matches[].matchScore |
float | AI match score (0.0 to 1.0) |
matches[].matchReasoning |
string | Detailed AI analysis with strengths, concerns, recommendation |
matches[].meets_all_must_haves |
boolean | True if candidate meets ALL identified must-have requirements |
matches[].must_haves_identified |
array | List of must-have requirements AI identified from JD |
matches[].bullhornId |
string | Bullhorn candidate ID (if synced from Bullhorn ATS) |
matches[].bullhornUrl |
string | Direct URL to open candidate in Bullhorn |
matches[].atsType |
string | ATS type (e.g., "bullhorn") - empty if not from ATS |
| Status | Error | Description |
|---|---|---|
| 401 | Authentication required |
Missing, expired, or invalid access token |
| 400 | q (or jobSpec) is required |
No job specification provided |
| 400 | jobLocation or jobLatitude/jobLongitude required |
No location provided for location-search |
| 400 | radiusKm must be 1..500 |
Invalid radius value |
| 400 | topN must be 1..100 |
Invalid topN value |
| 500 | Internal server error |
Server-side error occurred |
curl -X POST \
https://outreach.taladria.com/api/match-candidates \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-d '{
"jobSpec": "Senior PHP Developer with Laravel, 5+ years experience",
"topN": 5
}'
import requests
response = requests.post(
"https://outreach.taladria.com/api/match-candidates",
headers={
"Content-Type": "application/json",
"Authorization": "Bearer YOUR_ACCESS_TOKEN"
},
json={
"jobSpec": "Senior PHP Developer with Laravel, 5+ years experience",
"topN": 5
}
)
data = response.json()
for candidate in data["matches"]:
print(f"{candidate['name']}: {candidate['matchScore']*100:.0f}%")
const response = await fetch(
"https://outreach.taladria.com/api/match-candidates",
{
method: "POST",
headers: {
"Content-Type": "application/json",
"Authorization": "Bearer YOUR_ACCESS_TOKEN"
},
body: JSON.stringify({
jobSpec: "Senior PHP Developer with Laravel, 5+ years experience",
topN: 5
})
}
);
const data = await response.json();
data.matches.forEach(candidate => {
console.log(`${candidate.name}: ${(candidate.matchScore * 100).toFixed(0)}%`);
});
import requests
# Combined location and must-haves filtering (unified endpoint)
response = requests.post(
"https://outreach.taladria.com/api/match-candidates",
headers={
"Content-Type": "application/json",
"Authorization": "Bearer YOUR_ACCESS_TOKEN"
},
json={
"jobSpec": "Senior PHP Developer with Laravel",
"jobLocation": "Dublin, Ireland",
"radiusKm": 50,
"mustHaves": "5+ years PHP, Laravel experience",
"topN": 10
}
)
data = response.json()
print(f"Found {data['matchCount']} candidates within {data.get('radiusKm', 'N/A')}km")
for candidate in data["matches"]:
distance = candidate.get('distanceKm', 'N/A')
print(f"{candidate['name']}: {candidate['matchScore']*100:.0f}% - {distance}km away")
API requests are limited to 100 requests per minute per OAuth client. Contact us if you need higher limits.
For API support, integration assistance, or to request higher rate limits, contact us at info@taladria.com.