Guides
Canonical flow
Create the event, import the people, enrich them, run matching, read the results.
Nine calls, in this order, take you from nothing to a list of matches. Every one of them is copy-pasteable, and every long-running step is a run you poll rather than a request you wait on.
Prerequisites
Set the base URL and API key for the current docs hostname:
Production
export MESHI_BASE="https://api.meshi.io/api/v0/partner"
export MESHI_KEY="<MESHI_PARTNER_PRODUCTION_KEY>" Use this for live partner traffic.
Staging
export MESHI_BASE="https://api.staging.meshi.io/api/v0/partner"
export MESHI_KEY="<MESHI_PARTNER_STAGING_KEY>" Use this while building and validating an integration.
1. Confirm auth and write scope
curl -sS "$MESHI_BASE/auth" \
-H "Authorization: Bearer $MESHI_KEY"
Continue only if the response includes partner:write in scopes. If it includes event_id,
require that exact assigned event throughout this flow and skip event creation; event-scoped keys
cannot create another event or use org-wide routes.
2. Create the event
You own event_id. Use a stable ID from your event system.
curl -sS -X POST "$MESHI_BASE/events" \
-H "Authorization: Bearer $MESHI_KEY" \
-H "Content-Type: application/json" \
-d '{
"event_id": "summit-2026",
"title": "Annual Partner Summit 2026",
"description": "Two-day leadership summit",
"start_date": "2026-09-10",
"end_date": "2026-09-11",
"timezone": "America/New_York",
"visibility": "private",
"url_slug": "summit-2026"
}'
201 means the event was created. 409 PARTNER_ID_CONFLICT means that ID already exists in your org; fetch the event and continue if it is the same logical event.
3. Add matchmaking context
Context helps downstream matching use event-specific goals and instructions. Send at least one field.
curl -sS -X POST "$MESHI_BASE/events/summit-2026/context" \
-H "Authorization: Bearer $MESHI_KEY" \
-H "Content-Type: application/json" \
-d '{
"event_details": "Senior product and engineering leaders from portfolio companies.",
"objectives": "Maximize useful cross-company introductions.",
"welcome_instructions": "Introduce yourself with your top goal for the summit."
}'
Each context field is optional, but at least one must be present. Each value is capped at 4000 characters.
4. Import the attendees
Imports are async. Each attendee needs a stable attendee_id and name; email, LinkedIn URL, title, company, and raw data are optional.
IDEMP=$(uuidgen)
curl -sS -X PUT "$MESHI_BASE/events/summit-2026/attendees:import" \
-H "Authorization: Bearer $MESHI_KEY" \
-H "Content-Type: application/json" \
-d "{
\"idempotency_key\": \"$IDEMP\",
\"attendees\": [
{
\"attendee_id\": \"att-1001\",
\"name\": \"Jordan Lee\",
\"email\": \"jordan@example.com\",
\"linkedin_url\": \"https://www.linkedin.com/in/jordanlee\",
\"title\": \"VP Product\",
\"company\": \"Acme\",
\"side\": \"founder\",
\"event_goal\": \"Meet investors and enterprise design partners\",
\"raw_data\": { \"source\": \"crm-export-2026-06\" }
},
{
\"attendee_id\": \"att-1002\",
\"name\": \"Sam Rivera\",
\"title\": \"Head of Partnerships\",
\"company\": \"Globex\",
\"side\": \"investor\",
\"event_goal\": \"Meet founders building enterprise software\"
}
]
}"
The first accepted call returns 202. Capture run_id; the initial response has this shape:
{
"run_id": "<RUN_UUID>",
"kind": "import",
"status": "queued",
"event_id": "summit-2026",
"idempotency_key": "<THE_KEY_FROM_YOUR_REQUEST>",
"progress": {
"total": 2,
"processed": 0,
"created": 0,
"reused": 0,
"mapped": 0,
"event_added": 0,
"already_in_event": 0,
"review_required": 0,
"skipped": 0,
"error_count": 0,
"warnings": []
},
"error": null,
"idempotent_replay": false,
"correlation_id": "<CORRELATION_ID>"
}
A replay with the same idempotency key and event returns 200 with
idempotent_replay: true and the original run.
5. Poll the import run
Poll GET /runs/:run_id until status is terminal: succeeded, partial, or failed.
poll_run () {
local run_id="$1"
local delay=2
local max=30
while :; do
resp=$(curl -sS "$MESHI_BASE/runs/$run_id" \
-H "Authorization: Bearer $MESHI_KEY")
status=$(echo "$resp" | jq -r '.status')
case "$status" in
succeeded|partial|failed)
echo "$resp"
return 0
;;
*)
sleep "$delay"
if [ "$delay" -lt "$max" ]; then delay=$((delay * 2)); fi
if [ "$delay" -gt "$max" ]; then delay="$max"; fi
;;
esac
done
}
poll_run "<IMPORT_RUN_ID>"
For partial or failed, inspect progress and error. A representative partial import looks
like this:
{
"status": "partial",
"progress": {
"total": 2,
"processed": 2,
"created": 1,
"reused": 0,
"mapped": 1,
"event_added": 1,
"already_in_event": 0,
"review_required": 1,
"skipped": 0,
"error_count": 1,
"errors": [{
"row": 1,
"attendee_id": "att-1002",
"message": "Identity review required",
"review_id": "<REVIEW_UUID>"
}],
"warnings": []
},
"error": {
"code": "PARTIAL_IMPORT",
"message": "One or more attendee rows were held or rejected during import"
}
}
A held identity row includes a zero-based row, attendee_id, message, and review_id in
progress.errors. List the review
queue and resolve that review with accept, reject, or distinct; a reject returns
remaining_reviews, while rejecting the final candidate returns 409 REJECT_LAST_CANDIDATE and
leaves it open. Resolution applies the held import but does not change the terminal run from
partial to succeeded, so verify the roster/readiness afterward.
curl -sS "$MESHI_BASE/review-queue?limit=50" \
-H "Authorization: Bearer $MESHI_KEY"
curl -sS -X POST "$MESHI_BASE/review-queue/<REVIEW_ID>/resolve" \
-H "Authorization: Bearer $MESHI_KEY" \
-H "Content-Type: application/json" \
-d '{ "decision": "accept", "reason": "Confirmed by source system operator." }'
accept adopts the selected person, distinct creates a new one, and reject rejects one
candidate. Conflict-safe 409 responses leave the review open; relist before retrying.
Reuse the same idempotency key only for an identical request whose delivery is uncertain. A
terminal replay returns the existing run. Use a fresh key for corrected work after a terminal
partial or failed run.
6. Trigger enrichment
IDEMP=$(uuidgen)
curl -sS -X POST "$MESHI_BASE/events/summit-2026/enrichment-runs" \
-H "Authorization: Bearer $MESHI_KEY" \
-H "Content-Type: application/json" \
-d "{ \"idempotency_key\": \"$IDEMP\" }"
The response includes a queued run. Poll it to terminal with the same poll_run function.
Terminal enrichment means attendee-level work was queued; it does not mean every profile,
embedding, or intent derivation is ready.
Example enrichment progress:
{
"total": 2,
"processed": 2,
"enrichment_requested": 2,
"review_required": 0,
"skipped": 0,
"error_count": 0,
"errors": []
}
Rows with REVIEW_REQUIRED need identity review resolution. Rows with NO_SOURCE_RECORD had no source record available for enrichment.
Recheck GET /events/summit-2026/readiness after enrichment. Fix blocking warnings, missing
profile/intent inputs, and explicit side coverage before matching. For a two-sided event, require
side_coverage.policy_active: true; founder_count, investor_count, and
SEVERE_SIDE_IMBALANCE make roster capacity visible. intent_processing is a freshness signal: a match run
can use explicit event axes plus usable inferred profile intent while derivation is pending, or you
can wait for the newest derivation.
7. Trigger matching
IDEMP=$(uuidgen)
curl -sS -X POST "$MESHI_BASE/events/summit-2026/match-runs" \
-H "Authorization: Bearer $MESHI_KEY" \
-H "Content-Type: application/json" \
-d "{ \"idempotency_key\": \"$IDEMP\", \"top_n\": 10 }"
top_n is optional, defaults to 10, and must be between 1 and 50. Poll the returned match run to terminal. Trigger a fresh run after roster, side, or intent changes.
8. Read the matches
Read all matches for the event:
curl -sS "$MESHI_BASE/events/summit-2026/matches?limit=50&run_id=<MATCH_RUN_ID>" \
-H "Authorization: Bearer $MESHI_KEY"
Read top matches for one attendee:
curl -sS "$MESHI_BASE/events/summit-2026/attendees/att-1001/matches?limit=10&run_id=<MATCH_RUN_ID>&exclude_entity_ids[]=att-1002" \
-H "Authorization: Bearer $MESHI_KEY"
Read one ordered pair:
curl -sS "$MESHI_BASE/events/summit-2026/matches/att-1001/att-1002" \
-H "Authorization: Bearer $MESHI_KEY"
Match lists are ordered descending by final_score. Repeated exclusions accept partner attendee
IDs or event-roster entity_id handles and apply before pagination. If run_id is omitted, Meshi
uses the latest terminal match run; pin it while paging. Event-wide results support goal text but
not session_id.
9. Later reviews resolve the same way
Enrichment may surface additional identity reviews. Use the same list-and-resolve sequence from
the import recovery step; valid decisions remain accept, reject, and distinct.
Before you go live
- Use the production base URL and production key.
- Log every
correlation_id. - Generate one idempotency key per logical write operation, and reuse it only for retries of that same operation.
- Poll async runs to terminal; do not wait for webhook delivery in v0.
- Page through
next_cursorverbatim until it isnull. - On
429, honor theRetry-Afterheader before retrying.