API
Inbound enrichment and suppression
Send an inbound sponsorship lead, get it matched to a hackathon and organizer with a confidence score, and manage do-not-contact rules
Inbound enrichment takes a lead from your form, CRM or inbox, in whatever JSON shape you have, and tells you which hackathon and organizer it is about, how good that event looks, and whether you should contact them. Suppression rules are your account's do-not-contact list, checked on every submission. Both need an API key. See Authentication and keys.
Submit a lead
POST /api/v1/inbound/submit takes any JSON object, stores it as a submission on your account, and returns the match result with status 201.
curl -X POST https://www.hackathonradar.com/api/v1/inbound/submit \
-H "Authorization: Bearer $HACKATHONRADAR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"lead_id": "crm-000123",
"notes": "Sponsorship request from the website form",
"mapped": {
"event_url": "https://treelinehacks.example.com",
"event_title": "TreeLine Hacks",
"event_start_month": "2026-03",
"event_country": "United States",
"organizer_email": "team@treelinehacks.example.com",
"organizer_name": "Treeline Hacks"
}
}'mapped is optional. When you send it, only its seven fields are used for matching: event_url, event_title, event_start_month (YYYY-MM or any date), event_country (a country or a city), organizer_email, organizer_name and organizer_linkedin. Everything else in the body is stored untouched as raw_payload.
Without mapped, HackathonRadar reads common key names from the top level of the body. Key names are not case sensitive, and the underscore is optional, so eventLink and event_link both work.
| Field | Recognized keys |
|---|---|
| Event URL | url, event_url, website, link, event_link |
| Event title | title, event_title, name, event_name, hackathon |
| Start month | start_month, event_start_month, month, start_date, date |
| Country | country, event_country, location, region |
| Organizer email | email, organizer_email, contact_email |
| Organizer name | organizer_name, organizer, contact_name, name |
| Organizer LinkedIn | linkedin, organizer_linkedin, linkedin_url |
A bare name key fills both the event title and the organizer name. Send mapped when your keys are ambiguous.
Read the response
{
"submission_id": "76950fb0-2657-4e2d-9ebd-8003c4d34d70",
"match": {
"confidence": 0.95,
"explanation": [
"Event matched via url_exact (confidence: 0.95)",
"Organizer matched via email_exact (confidence: 0.95)"
],
"event_id": "6fa487e4-c891-456f-81f1-2a4bedc12967",
"organizer_id": "76ee2e74-a0b0-4f19-85d9-30f9d2c3f181",
"event_match_method": "url_exact",
"organizer_match_method": "email_exact",
"organizer_inferred_from_event": false
},
"event": {
"title": "TreeLine Hacks",
"start_at": "2026-03-06T16:45:00.000Z",
"country": null,
"city": null,
"format": "online",
"quality_score": 55,
"quality_breakdown": {}
},
"organizer": {
"name": "Treeline Hacks",
"email": "team@treelinehacks.example.com",
"num_events_seen": 3,
"domain_events_seen": 3,
"first_seen_at": "2025-01-10T12:00:00.000Z",
"last_seen_at": "2026-03-06T16:45:00.000Z"
},
"suppression": { "is_duplicate_inbound": false, "is_suppressed": false, "reasons": [] }
}| Field | What it tells you |
|---|---|
match.confidence | How sure the match is, from 0 to 1 |
match.explanation | Each matching step in plain text, ready to show to a sales rep |
match.event_id, match.organizer_id | The matched hackathon and organizer, or null. Fetch them in full from the hackathon and organizer endpoints |
match.organizer_inferred_from_event | true when the organizer was taken from the matched event rather than from the lead |
event | The matched hackathon's basics and its quality score, or null |
organizer | The matched organizer, or null. num_events_seen counts their events and domain_events_seen counts events across every organizer on the same domain. first_seen_at is when HackathonRadar first recorded them and last_seen_at is the start of their latest event |
suppression | Whether the lead is a duplicate or matches one of your suppression rules, with the reasons |
Match methods
event_match_method and organizer_match_method name what the match was based on, or are null when nothing matched.
| Value | Side | Based on |
|---|---|---|
url_exact | Event | The event's website or registration URL |
source_urls_containment | Event | One of the event's listing URLs |
source_id_exact | Event | The event's listing ID, read from the URL |
title_country_date | Event | Title, location and date |
title_date | Event | Title and date |
title_fuzzy_date | Event | A similar title and the date |
title_fuzzy | Event | A similar title |
email_exact | Organizer | Email address |
linkedin_exact | Organizer | LinkedIn profile |
name_fuzzy | Organizer | A similar name |
event_inferred | Organizer | The matched event's organizers |
Duplicates and statuses
A lead is a duplicate when your account sent the same lead in the last 90 days. Duplicates are still matched and stored. Each stored submission has a status:
status | When |
|---|---|
duplicate | The same lead was sent in the last 90 days. Takes precedence over the others |
suppressed | A suppression rule matched |
matched | An event matched |
unmatched | No event matched |
Review stored submissions
GET /api/v1/inbound/submissionslists your submissions, newest first. Parameters:status,page(default 1) andlimit(default 25, at most 100). The response holdsdata,page,limit,total,totalPages,hasNextandhasPrev.GET /api/v1/inbound/submissions/{id}returns one stored submission with the raw payload, the fields read from it, the match result, the quality snapshot and any suppression reasons. An id from another account returns 404Submission not found.GET /api/v1/inbound/submissions/exportreturns all your submissions, or those with onestatus, asinbound-submissions.csv.
Suppression rules
POST /api/v1/suppression/add creates a rule and returns it with status 201. Sending the same suppression_type and value again updates its reason and expires_at.
curl -X POST https://www.hackathonradar.com/api/v1/suppression/add \
-H "Authorization: Bearer $HACKATHONRADAR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"suppression_type": "domain", "value": "gmail.com", "reason": "Skip personal inboxes", "expires_at": null}'suppression_type | Matches |
|---|---|
email | The lead's organizer email |
domain | The domain of the lead's organizer email |
event_url | The lead's event URL |
Submissions are compared in a cleaned-up form: emails in lowercase, and event URLs with https, a lowercase host, no trailing slash or fragment, and tracking parameters such as utm_* removed. Store rule values in that form. A rule stops applying once its expires_at has passed. To lift a rule, add it again with an expires_at in the past.
POST /api/v1/suppression/check runs the same check without storing anything. Send organizer_email and event_url. The check compares values exactly as you send them, so lowercase emails and clean URLs first.
{ "is_suppressed": true, "reasons": ["Suppressed: domain \"gmail.com\""] }Each reason names the rule, followed by your reason text when the rule has one.