Skip to content

Endpoint Reference

All paths are relative to the Partner API base URL. The scope listed for each endpoint is the minimum effective scope.

Every response body includes correlation_id. Every error uses the shared error envelope described in Errors and Limits.

Async run endpoints accept webhook_url for forward compatibility, but v0 does not deliver webhook callbacks. Poll GET /runs/:run_id for terminal status.

When GET /auth returns event_id, the key is restricted to that assigned event. Event-shaped requests for another event return 404 EVENT_NOT_FOUND. Org-wide writes that are unnecessary for the assigned-event workflow return 403 EVENT_SCOPE_FORBIDDEN; event, run, review, and ID-map lists are narrowed to the assigned event as described below.

Scope: partner:read

Returns authenticated service-account details.

Response 200:

{
"org_id": "<ORG_ID>",
"principal_type": "service_account",
"service_account_user_id": "<SERVICE_ACCOUNT_USER_ID>",
"scopes": ["partner:read", "partner:write"],
"environment": "production",
"event_id": "summit-2026",
"capabilities": {
"events_read": true,
"attendees_import": true,
"attendees_delete": true,
"attendees_side_retag": true,
"people_search": true
},
"correlation_id": "<CORRELATION_ID>"
}

Route-specific errors: auth and scope errors only.

Scope: partner:read

Returns { org_id, scopes, environment, event_id?, capabilities, correlation_id }. event_id is present for an assigned-event key. Capability booleans describe enabled route families; they do not override the assigned event boundary.

Scope: partner:read

Confirms whether a partner ID is mapped in your org.

Path parameters:

NameDescription
kindPartner ID kind, such as event, session, or attendee.
partner_idPartner-owned ID.

Response 200:

{
"kind": "attendee",
"partner_id": "att-1001",
"mapped": true,
"correlation_id": "<CORRELATION_ID>"
}

Errors: 400 INVALID_PARTNER_KIND, 400 INVALID_PARTNER_ID, 404 PARTNER_ID_NOT_FOUND.

Scope: partner:write

Creates an event. You own event_id.

Request:

{
"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"
}

Field limits:

FieldLimit
titleRequired, max 200 characters.
descriptionOptional, max 4000 characters.
start_date, end_dateOptional ISO date strings.
timezoneOptional, max 128 characters.
visibilityOptional, max 32 characters.
url_slugOptional, max 128 characters.

Response 201: event object with event_id, details, timestamps, and correlation_id.

Errors: 400 INVALID_JSON, 400 INVALID_PARTNER_ID, 400 INVALID_TITLE, 400 INVALID_DESCRIPTION, 400 INVALID_START_DATE, 400 INVALID_END_DATE, 400 INVALID_TIMEZONE, 400 INVALID_VISIBILITY, 400 INVALID_URL_SLUG, 409 PARTNER_ID_CONFLICT.

Scope: partner:read

Lists events ordered descending by created_at.

Query parameters: limit (1-100, default 50), cursor.

Response 200:

{
"items": [
{
"event_id": "summit-2026",
"title": "Annual Partner Summit 2026",
"description": "Two-day leadership summit",
"event_details": null,
"objectives": null,
"welcome_instructions": null,
"start_date": "2026-09-10",
"end_date": "2026-09-11",
"timezone": "America/New_York",
"visibility": "private",
"url_slug": "summit-2026",
"created_at": "<ISO_DATETIME>",
"updated_at": "<ISO_DATETIME>"
}
],
"next_cursor": null,
"correlation_id": "<CORRELATION_ID>"
}

Errors: org, auth, scope, and rate-limit errors.

Scope: partner:read

Returns one event.

Response 200: event object with context fields, timestamps, and correlation_id.

Errors: 400 INVALID_PARTNER_ID, 404 EVENT_NOT_FOUND.

Scope: partner:write

Updates one or more event metadata fields: title, description, dates, timezone, visibility, URL slug, event details, objectives, or welcome instructions. Response 200 is the updated event. Errors: 400 INVALID_PARTNER_ID, 400 INVALID_JSON, 400 INVALID_TITLE, 400 INVALID_EVENT_UPDATE, 400 INVALID_START_DATE, 400 INVALID_END_DATE, 400 UNSUPPORTED_QUERY_PARAMETER, 404 EVENT_NOT_FOUND, and 409 URL_SLUG_CONFLICT.

Scope: partner:write

Soft-deletes an event. Use dry_run=true to preview affected relationships; force=true is required when protected relationships remain. Response 200 includes affected counts and deletion/preview state. Errors: 400 INVALID_PARTNER_ID, 400 UNSUPPORTED_QUERY_PARAMETER, 404 EVENT_NOT_FOUND, and 409 EVENT_NOT_EMPTY.

Scope: partner:write

Updates event context. At least one field is required.

Request:

{
"event_details": "Senior product and engineering leaders.",
"objectives": "Maximize useful introductions.",
"welcome_instructions": "Share your top goal for the event."
}

Each field is optional and capped at 4000 characters.

Response 200:

{
"event_id": "summit-2026",
"context": {
"event_details": "Senior product and engineering leaders.",
"objectives": "Maximize useful introductions.",
"welcome_instructions": "Share your top goal for the event."
},
"correlation_id": "<CORRELATION_ID>"
}

Errors: 400 INVALID_PARTNER_ID, 400 INVALID_JSON, 400 INVALID_CONTEXT, 404 EVENT_NOT_FOUND.

Scope: partner:read

Returns event context.

Response 200: { event_id, context, correlation_id }.

Errors: 400 INVALID_PARTNER_ID, 404 EVENT_NOT_FOUND.

Scope: partner:write

Creates a session within an event. You own session_id.

Request:

{
"session_id": "opening-keynote",
"title": "Opening Keynote",
"description": "Kickoff session",
"start_timestamp": "2026-09-10T13:00:00Z",
"end_timestamp": "2026-09-10T14:00:00Z"
}

Response 201: session object with session_id, event_id, metadata, timestamps, and correlation_id.

Errors: 400 INVALID_PARTNER_ID, 400 INVALID_TITLE, 400 INVALID_DESCRIPTION, 400 INVALID_START_TIMESTAMP, 400 INVALID_END_TIMESTAMP, 404 EVENT_NOT_FOUND, 409 PARTNER_ID_CONFLICT.

Scope: partner:read

Lists event sessions ordered ascending by start_timestamp. Optional filters apply before pagination; keep the same filters when following a next_cursor.

Query parameters: limit, cursor, q, starts_after, starts_before.

ParameterDescription
qOptional case-insensitive keyword filter over title and description.
starts_afterOptional inclusive lower bound for start_timestamp.
starts_beforeOptional inclusive upper bound for start_timestamp.

Response 200: { items, next_cursor, correlation_id }.

Errors: 400 INVALID_PARTNER_ID, 400 INVALID_SESSION_QUERY, 400 INVALID_STARTS_AFTER, 400 INVALID_STARTS_BEFORE, 404 EVENT_NOT_FOUND.

GET /events/:event_id/sessions/:session_id

Section titled “GET /events/:event_id/sessions/:session_id”

Scope: partner:read

Returns one session.

Response 200: session object with correlation_id.

Errors: 400 INVALID_PARTNER_ID, 400 UNSUPPORTED_QUERY_PARAMETER, and 404 SESSION_NOT_FOUND.

PATCH /events/:event_id/sessions/:session_id

Section titled “PATCH /events/:event_id/sessions/:session_id”

Scope: partner:write

Updates session title, description, start/end timestamps, or metadata. Response 200 is the updated session. Errors: 400 INVALID_PARTNER_ID, 400 INVALID_JSON, 400 INVALID_TITLE, 400 INVALID_DESCRIPTION, 400 INVALID_START_TIMESTAMP, 400 INVALID_END_TIMESTAMP, 400 INVALID_METADATA, 400 INVALID_SESSION_UPDATE, 400 UNSUPPORTED_QUERY_PARAMETER, and 404 SESSION_NOT_FOUND.

DELETE /events/:event_id/sessions/:session_id

Section titled “DELETE /events/:event_id/sessions/:session_id”

Scope: partner:write

Soft-deletes a session. Use dry_run=true to preview. The response reports the current session-participant count but does not delete those participant rows. Response 200 includes affected counts. Errors: 400 INVALID_PARTNER_ID, 400 UNSUPPORTED_QUERY_PARAMETER, and 404 SESSION_NOT_FOUND.

Scope: partner:read

Lists every event attendee, including people added internally before a partner attendee ID existed. Rows with partner IDs come first, followed by internally added rows ordered by Meshi entity ID. Keep the returned cursor opaque.

Query parameters: limit, cursor.

Response 200:

{
"items": [
{
"attendee_id": "att-1001",
"entity_id": "11111111-1111-4111-8111-111111111111",
"id_source": "partner",
"event_id": "summit-2026",
"display_name": "Jordan Lee",
"title": "VP Product",
"company": "Acme",
"event": {
"role_at_event": null,
"side": "founder",
"visibility": null,
"goal": null,
"needs": null,
"offers": null,
"onboarded_at": null,
"joined_at": null
},
"identity": {
"review_required": false,
"open_review_count": 0
}
}
],
"next_cursor": null,
"correlation_id": "<CORRELATION_ID>"
}

attendee_id is null and id_source is "meshi" for an internally added attendee with no partner mapping. Use its entity_id in match exclusions. A mapped attendee also includes entity_id, but partner integrations should keep using their stable attendee_id wherever an endpoint path asks for one.

event.side is the matching-side tag at a two-sided event: "founder", "investor", or null. event.role_at_event is descriptive only.

Errors: 400 INVALID_PARTNER_ID, 404 EVENT_NOT_FOUND.

GET /events/:event_id/attendees/:attendee_id

Section titled “GET /events/:event_id/attendees/:attendee_id”

Scope: partner:read

Returns partner-safe enriched attendee detail. Optional comma-separated include values select profile, event, identity, brief, traits, goals, or readiness. This path requires a partner attendee_id; the event-roster UUID exception does not apply. Errors include 400 INVALID_INCLUDE, 404 EVENT_NOT_FOUND, and 404 ATTENDEE_NOT_FOUND.

PATCH /events/:event_id/attendees/:attendee_id

Section titled “PATCH /events/:event_id/attendees/:attendee_id”

Scope: partner:write

Corrects an attendee’s matching side without changing their event role. The path accepts either a partner attendee ID or an entity_id returned by the event roster. The update removes that attendee’s directional event scores and queues a durable rescore fanout. A cleared side remains intentionally unmatchable; Meshi never infers a replacement.

Request:

{
"side": "founder"
}

side must be "founder", "investor", or null. This side-correction body accepts no other fields. To change event intent, re-import the stable attendee_id with the updated intent and a fresh idempotency key after any prior run is terminal.

Response 200: { event_id, attendee_id, entity_id, id_source, side, correlation_id }.

Errors: 400 INVALID_PARTNER_ID, 400 INVALID_JSON, 400 INVALID_ATTENDEE_SIDE, 400 INVALID_ATTENDEE_UPDATE, 404 EVENT_NOT_FOUND, 404 ATTENDEE_NOT_FOUND.

Scope: partner:write

Imports attendees into an event. Async; returns a run.

Request:

{
"idempotency_key": "<IDEMPOTENCY_KEY>",
"webhook_url": "https://example.com/future-webhook",
"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": "Raise our seed round",
"event_needs": ["intros to seed investors"],
"event_offers": ["developer-tools product with strong growth"],
"raw_data": { "source": "crm-export" }
}
]
}

Per-attendee limits:

FieldLimit
nameRequired, max 240 characters.
emailOptional, max 320 characters.
linkedin_urlOptional, max 2048 characters.
titleOptional, max 512 characters.
companyOptional, max 512 characters.
sideOptional. "founder", "investor", or null. Tags the attendee’s matching side at two-sided events; an absent or null side leaves the attendee unmatchable at a sided event (fail-closed) until tagged. See the two-sided matching guide.
event_goalOptional, max 4000 characters. Plain-language event context. Meshi derives useful needs and evidence-backed offers when possible. Without it, Meshi falls back to usable inferred or confirmed profile intent; readiness reports a blocker if neither source exists.
event_needsOptional array of strings. Explicit event-specific needs override inferred needs for this axis. Usually omit this unless the attendee supplied it directly.
event_offersOptional array of strings. Explicit event-specific offers override inferred offers for this axis. Usually omit this unless the attendee supplied them directly; Meshi will not invent an offer from a goal without supporting profile evidence.
raw_dataOptional JSON object.

Send stable identity fields such as email and LinkedIn URL whenever available; they improve identity resolution and inferred intent. Every accepted import reapplies side; omitting it or sending null deliberately untags that attendee. Supplied event-intent axes update the corresponding values, while omitted axes preserve their existing values. Use PATCH /events/:event_id/attendees/:attendee_id for a one-off side correction without starting another import run.

The attendees array must be non-empty, contain unique attendee_id values, and contain at most 50,000 rows by default.

webhook_url is stored on the run for future compatibility, but v0 does not deliver webhooks. Poll GET /runs/:run_id for terminal status.

Response 202 first call or 200 idempotent replay: run object with kind: "import".

Initial accepted response (timestamps and metadata omitted here for readability):

{
"run_id": "<RUN_UUID>",
"kind": "import",
"status": "queued",
"event_id": "summit-2026",
"idempotency_key": "<THE_KEY_FROM_YOUR_REQUEST>",
"progress": {
"total": 1,
"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>"
}

Terminal import progress includes counters, warnings, and optional per-row errors. Each error contains zero-based row, attendee_id, and message; a held identity row also contains an actionable review_id. Resolve it through the review queue, then recheck the roster/readiness. The terminal run remains partial. Reuse the original idempotency key only for an identical delivery retry; use a new key for corrected work after a terminal partial or failed run.

Errors: 400 INVALID_PARTNER_ID, 400 INVALID_JSON, 400 INVALID_IDEMPOTENCY_KEY, 400 INVALID_ATTENDEES, 400 INVALID_ATTENDEE, 400 DUPLICATE_ATTENDEE_ID, 400 INVALID_ATTENDEE_NAME, 400 INVALID_ATTENDEE_EMAIL, 400 INVALID_ATTENDEE_LINKEDIN_URL, 400 INVALID_ATTENDEE_TITLE, 400 INVALID_ATTENDEE_COMPANY, 400 INVALID_ATTENDEE_SIDE, 400 INVALID_ATTENDEE_EVENT_GOAL, 400 INVALID_ATTENDEE_EVENT_NEEDS, 400 INVALID_ATTENDEE_EVENT_OFFERS, 400 INVALID_ATTENDEE_RAW_DATA, 400 INVALID_WEBHOOK_URL, 404 EVENT_NOT_FOUND, 409 IDEMPOTENCY_KEY_CONFLICT, 413 PAYLOAD_TOO_LARGE.

DELETE /events/:event_id/attendees/:attendee_id

Section titled “DELETE /events/:event_id/attendees/:attendee_id”

Scope: partner:write

Removes an attendee from an event and from that event’s sessions. This is reversible relationship cleanup, not person erasure: the org-level person record and partner ID mapping are retained for idempotent re-import and auditability.

Full privacy erasure is not self-service in v0. Coordinate it with the Meshi implementation contact assigned during handoff, including the attendee ID and correlation_id.

Query parameters: dry_run=true to preview counts without mutation.

deleted_at is present only when the request removed at least one event or session relationship. Retry-safe no-ops return already_absent: true without deleted_at.

Response 200:

{
"event_id": "summit-2026",
"attendee_id": "att-1001",
"deleted_at": "<ISO_DATETIME>",
"already_absent": false,
"retained_person": true,
"affected": {
"event_attendees": 1,
"session_participants": 2
},
"correlation_id": "<CORRELATION_ID>"
}

Errors: 400 INVALID_PARTNER_ID, 404 EVENT_NOT_FOUND, 404 ATTENDEE_NOT_FOUND.

GET /events/:event_id/attendees/:attendee_id/identity

Section titled “GET /events/:event_id/attendees/:attendee_id/identity”

Scope: partner:read

Returns identity decision history for one attendee, ordered descending by created_at.

Query parameters: limit, cursor.

Response 200: { event_id, attendee_id, items, next_cursor, correlation_id }.

Errors: 400 INVALID_PARTNER_ID, 404 EVENT_NOT_FOUND, 404 ATTENDEE_NOT_FOUND.

Scope: partner:write

Searches only people in one event. Request { query, mode?, type?, limit? }; response { event_id, query, mode, type, items, correlation_id }. Errors include 400 INVALID_QUERY, 400 INVALID_SEARCH_MODE, 400 INVALID_SEARCH_TYPE, 404 EVENT_NOT_FOUND, and 503 SEARCH_NOT_CONFIGURED.

Scope: partner:write

Searches people across the authenticated org with the same body and response shape, excluding event_id. Assigned-event keys cannot call this org-wide route and receive 403 EVENT_SCOPE_FORBIDDEN.

GET /events/:event_id/attendees/:attendee_id/readiness

Section titled “GET /events/:event_id/attendees/:attendee_id/readiness”

Scope: partner:read

Diagnostics for whether one attendee is ready for search and matching.

Response 200: { event_id, attendee_id, readiness: { match_ready, global_match_ready, event_match_ready, missing, recommended_actions }, correlation_id }.

missing names the specific prerequisites still absent (for example a brief embedding or event goal embeddings); recommended_actions says what to do about each.

Errors: 400 INVALID_PARTNER_ID, 404 EVENT_NOT_FOUND, 404 ATTENDEE_NOT_FOUND.

Scope: partner:read

Paged readiness diagnostics for all event attendees, plus side-tag coverage for two-sided events.

Query parameters: limit, cursor.

Response 200: { items: [{ event_id, attendee_id, entity_id, id_source, readiness }], side_coverage: { policy_active, founder_count, investor_count, untagged_count, untagged_attendee_ids, untagged_entity_ids }, intent_processing: { pending_count, pending_entity_ids, failed_count, failed_entity_ids }, match_readiness: { ready_count, not_ready_count }, warnings: [{ code, severity, message, recommended_action }], next_cursor, correlation_id }.

side_coverage reports full-roster counts and lists every attendee with no valid side tag. The partner-ID array contains mapped attendees; untagged_entity_ids also contains internally added attendees. At a two-sided event those attendees receive no matches until tagged — check this before opening match browsing. See the two-sided matching guide.

For an intended two-sided event, require side_coverage.policy_active: true. Top-level warnings promote missing sides, untagged people, severe side imbalance, and event-wide match-readiness gaps. Treat severity: "blocking" as a stop sign. match_readiness counts the full roster regardless of pagination; each paged item explains its own missing profile or intent inputs.

intent_processing is a freshness signal, not a match-run lock. Pending or failed machine-derived axes fall back to selected profile intent, while explicit event axes remain usable. Wait for pending_count: 0 only when you want the newest derived intent reflected in the next snapshot. Per-attendee readiness.missing uses event_intent_processing while work is pending and event_intent_failed after a terminal failure.

Errors: 400 INVALID_PARTNER_ID, 404 EVENT_NOT_FOUND.

Scope: partner:write

Triggers attendee enrichment for an event. Async; returns a run.

Request:

{
"idempotency_key": "<IDEMPOTENCY_KEY>",
"webhook_url": "https://example.com/future-webhook"
}

Response 202 first call or 200 idempotent replay: run object with kind: "enrichment" and progress counters.

Errors: 400 INVALID_PARTNER_ID, 400 INVALID_JSON, 400 INVALID_IDEMPOTENCY_KEY, 400 INVALID_WEBHOOK_URL, 404 EVENT_NOT_FOUND, 409 IDEMPOTENCY_KEY_CONFLICT.

Scope: partner:write

Triggers an attendee matching run. Async; returns a run.

Request:

{
"idempotency_key": "<IDEMPOTENCY_KEY>",
"webhook_url": "https://example.com/future-webhook",
"top_n": 10
}

top_n is optional, defaults to 10, and must be 1-50.

Response 202 first call or 200 idempotent replay: run object with kind: "match".

Errors: 400 INVALID_PARTNER_ID, 400 INVALID_JSON, 400 INVALID_IDEMPOTENCY_KEY, 400 INVALID_WEBHOOK_URL, 404 EVENT_NOT_FOUND, 409 IDEMPOTENCY_KEY_CONFLICT.

Match-run creation remains available while derived event intent is processing. The run uses explicit event axes plus profile-intent fallback; a later run picks up completed derivations.

Match results are pinned snapshots of a match run — identical queries against the same run never contradict each other. After roster changes, side re-tags, or goal edits, trigger a new match run to refresh results.

GET /events/:event_id/attendees/:attendee_id/matches

Section titled “GET /events/:event_id/attendees/:attendee_id/matches”

Scope: partner:read

Lists top matches for one attendee, ordered descending by final_score.

attendee_id may be the partner attendee ID or the event-roster entity_id returned by GET /events/:event_id/attendees. This lets an organizer read matches for internally added attendees that do not have a partner mapping.

Query parameters: limit, cursor, optional run_id, repeatable exclude_entity_ids[]. If run_id is omitted, the latest terminal run is used.

exclude_entity_ids[] omits specific attendees from the results (for example, people the requester has already met or plans to meet). It accepts either partner attendee IDs or the entity_id returned by the event attendee list, applies before pagination (pages stay full-length and cursors stay stable), and is capped at 500 values; exceeding the cap returns 400 TOO_MANY_EXCLUDE_ENTITY_IDS. A Meshi entity ID is accepted only when that person belongs to this event. Unknown or out-of-event IDs are ignored without revealing whether they exist.

Response 200: { event_id, attendee_id, entity_id, id_source, run_id, items, next_cursor, correlation_id }.

Every match item includes both entity IDs and an ID source for each side. For a pre-existing person with no partner mapping, the corresponding *_attendee_id is null and *_id_source is "meshi". Match runs include these attendees as principals and targets.

Each match item includes an explainability object:

{
"explainability": {
"tier": "exceptional",
"reasons": [
{ "kind": "aggregate_needs_to_offers", "text": "Their needs align with your offers across their event goals." },
{ "kind": "best_goal_pair", "text": "Your goal pairs strongly with one of theirs." }
]
}
}

tier is a calibrated presentation tier (exceptional, strong, promising, explore). reasons are readable, non-numeric explanations framed around each side’s stated needs and offers — lead product presentation with the tier and reasons; the raw sub_scores remain available for compatibility. Runs recorded before explainability shipped return a tier with an empty reasons array.

Errors: 400 INVALID_PARTNER_ID, 400 TOO_MANY_EXCLUDE_ENTITY_IDS, 404 EVENT_NOT_FOUND, 404 ATTENDEE_NOT_FOUND, 404 MATCH_RUN_NOT_FOUND.

Scope: partner:read

Lists matches across all attendee pairs for an event, ordered descending by final_score.

Query parameters: limit, cursor, optional run_id, optional goal, repeatable exclude_entity_ids[] (same semantics and cap as the per-attendee endpoint). session_id is not supported; unsupported parameters return 400 UNSUPPORTED_QUERY_PARAMETER with supported_parameters.

Response 200: { event_id, run_id, items, next_cursor, correlation_id }.

Errors: 400 INVALID_PARTNER_ID, 400 UNSUPPORTED_QUERY_PARAMETER, 400 TOO_MANY_EXCLUDE_ENTITY_IDS, 404 EVENT_NOT_FOUND, and 404 MATCH_RUN_NOT_FOUND.

GET /events/:event_id/matches/:principal_partner_id/:target_partner_id

Section titled “GET /events/:event_id/matches/:principal_partner_id/:target_partner_id”

Scope: partner:read

Returns one ordered pair match score. Each path identifier may be a partner attendee ID or an event-roster entity_id.

Query parameters: optional run_id, repeatable exclude_entity_ids[]. If the requested target is excluded, the response is 404 MATCH_NOT_FOUND.

Response 200:

{
"event_id": "summit-2026",
"match_id": "<MATCH_ID>",
"run_id": "<RUN_ID>",
"principal_attendee_id": "att-1001",
"principal_entity_id": "<PRINCIPAL_ENTITY_ID>",
"principal_id_source": "partner",
"target_attendee_id": "att-1002",
"target_entity_id": "<TARGET_ENTITY_ID>",
"target_id_source": "partner",
"final_score": 0.87,
"sub_scores": {
"goal_complementarity": 0.9,
"goal_needs_to_offers": 0.8,
"goal_offers_to_needs": 0.7,
"value_alignment": 0.6,
"general_similarity": 0.5,
"score_a_to_b": 0.85,
"score_b_to_a": 0.82
},
"best_goal_match": { "score": 0.9, "direction": "needsToOffers" },
"driving_goal_pair": { "score": 0.9, "direction": "needsToOffers" },
"intent_strength": { "aToB": 0.6, "bToA": 0.4 },
"input_hash": "<INPUT_HASH>",
"algorithm_version": "<ALGORITHM_VERSION>",
"policy_version": "<POLICY_VERSION>",
"context_snapshot_id": "<CONTEXT_SNAPSHOT_ID>",
"freshness": { "computed_at": "<ISO_DATETIME>" },
"computed_at": "<ISO_DATETIME>",
"correlation_id": "<CORRELATION_ID>"
}

Errors: 400 INVALID_PARTNER_ID, 400 TOO_MANY_EXCLUDE_ENTITY_IDS, 404 EVENT_NOT_FOUND, 404 ATTENDEE_NOT_FOUND, 404 MATCH_RUN_NOT_FOUND, and 404 MATCH_NOT_FOUND.

Goal IDs are not exposed. best_goal_match and driving_goal_pair surface score and direction only. When both directions have usable goal text, a company-framed best_goal_pair explainability reason may include that readable text without internal IDs.

Scope: partner:write

Creates a generic run outside event scope.

Request:

{
"kind": "import",
"idempotency_key": "<IDEMPOTENCY_KEY>",
"progress": {},
"snapshot_metadata": {},
"version_metadata": {},
"webhook_url": "https://example.com/future-webhook"
}

kind must be import, enrichment, or match.

Response 201 first call or 200 idempotent replay: run object. event_id is null for non-event runs.

Errors: 400 INVALID_JSON, 400 INVALID_RUN_KIND, 400 INVALID_IDEMPOTENCY_KEY, 400 INVALID_PROGRESS, 400 INVALID_SNAPSHOT_METADATA, 400 INVALID_VERSION_METADATA, 400 INVALID_WEBHOOK_URL.

Scope: partner:read

Lists all runs for the org, ordered descending by created_at.

Query parameters: limit, cursor, optional kind.

Response 200: { items, next_cursor, correlation_id }.

Errors: 400 INVALID_RUN_KIND.

Scope: partner:read

Polls one run. This is the canonical terminal-status poll for import, enrichment, and match runs. For kind: "import", terminal progress uses the import counters and per-row errors documented under attendee import, including an actionable review_id for a held identity row. Other run kinds retain their documented progress objects.

Path parameters: run_id.

Response 200:

{
"run_id": "<RUN_ID>",
"kind": "match",
"status": "queued",
"event_id": "summit-2026",
"idempotency_key": "<IDEMPOTENCY_KEY>",
"progress": {},
"error": null,
"snapshot_metadata": {},
"version_metadata": {},
"webhook_url": null,
"created_at": "<ISO_DATETIME>",
"updated_at": "<ISO_DATETIME>",
"started_at": null,
"terminal_at": null,
"correlation_id": "<CORRELATION_ID>"
}

Errors: 400 INVALID_RUN_ID, 404 RUN_NOT_FOUND.

Scope: partner:read

Lists open identity reviews for one event’s attendees, ordered descending by created_at.

Query parameters: limit, cursor.

Response 200: { items: [{ review_id, kind, partner_id, display_name, candidate: { display_name, title, company, linkedin_url }, source_path, candidate_ref, confidence, reasoning, evaluator, created_at }], next_cursor, correlation_id }.

Errors: 400 INVALID_PARTNER_ID, 404 EVENT_NOT_FOUND.

Scope: partner:read

Lists open identity reviews across the org, ordered descending by created_at.

Query parameters: limit, cursor.

Response 200: { items: [{ review_id, kind, partner_id, display_name, candidate: { display_name, title, company, linkedin_url }, source_path, candidate_ref, confidence, reasoning, evaluator, created_at }], next_cursor, correlation_id }.

candidate gives the organizer safe comparison details for the proposed existing person. It deliberately omits email addresses and Meshi internal IDs. Accept only if the available identity evidence supports the same person; reject a clearly different candidate, then inspect remaining_reviews, or choose distinct when no candidate is the imported person.

Route-specific errors: auth and scope errors only.

Scope: partner:write

Accepts, rejects, or marks an open review as a distinct person.

Request:

{
"decision": "accept",
"reason": "Confirmed by source system operator."
}

decision must be accept, reject, or distinct.

Response 200: { review_id, kind, partner_id, decision, logged: true, minted?, remaining_reviews?, enrichment_requeued: { enrichment_requested, skipped }, correlation_id }.

For a held import, accept adopts the selected existing person and distinct mints a new one; both map the original attendee ID and apply imported event data. reject rejects one candidate and returns remaining_reviews. Rejecting the final candidate returns 409 REJECT_LAST_CANDIDATE without consuming it. Conflict-safe 409 codes REVIEW_ALREADY_RESOLVED, ATTENDEE_ID_CONFLICT, IDENTITY_CONFLICT, and DISTINCT_IDENTITY_CONFLICT also leave the review open; relist before retrying.

Errors: 400 INVALID_REVIEW_ID, 400 INVALID_JSON, 400 INVALID_REVIEW_DECISION, 404 REVIEW_NOT_FOUND, and the conflict-safe 409 codes above.

Scope: partner:write

Resolves a review by merging the source entity into the target entity.

Request:

{
"source_kind": "attendee",
"source_partner_id": "att-dupe",
"target_kind": "attendee",
"target_partner_id": "att-1001",
"reason": "Same person in source system."
}

Response 200: { review_id, source, target, merged: true, superseded_anchors, rewritten_anchors, deleted_computations, correlation_id }.

Errors: 400 INVALID_REVIEW_ID, 400 INVALID_JSON, 400 INVALID_PARTNER_KIND, 400 INVALID_PARTNER_ID, 400 REVIEW_SOURCE_MISMATCH, 400 MERGE_REJECTED, 404 REVIEW_NOT_FOUND, 404 PARTNER_ID_NOT_FOUND.