Canonical Flow
This is the recommended end-to-end sequence for a partner integration.
Prerequisites
Section titled “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
Section titled “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
Section titled “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
Section titled “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 Attendees
Section titled “4. Import 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
Section titled “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
Section titled “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
Section titled “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 Matches
Section titled “8. Read 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. Resolve Later Reviews the Same Way
Section titled “9. Resolve Later Reviews 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.
Production Checklist
Section titled “Production Checklist”- 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.