## TL;DR

Resolve the email to a Salesforce User Id before assigning: query the User object by email, take the returned Id, and put that Id in OwnerId. Never write an email string into OwnerId - it is a reference field and only accepts a User or Queue Id.

```text
salesforce INVALID_CROSS_REFERENCE_KEY when agent assigns lead owner by email instead of user id
```

## Steps

1. Query the User object filtering by email to get the matching user's Id. Expected: the query returns exactly one active User row with its Id.

2. Use that Id as the OwnerId value on the lead create or update call. Expected: the API returns success instead of INVALID_CROSS_REFERENCE_KEY.

3. Cache the email-to-Id mapping for the duration of the run so you do not re-query per lead. Expected: repeat assignments for the same owner hit the cache.

4. Handle the no-match case: fall back to a default owner or queue and log the unmapped email for review. Expected: unmappable emails route to the fallback instead of failing the batch.

## Use this when

- lead owner assignment by email fails with INVALID_CROSS_REFERENCE_KEY
- the agent sets OwnerId to an email string
- owner assignment works in the UI but fails over the API

## Not for this skill when

- assignment rules doing owner routing declaratively - those do not take an OwnerId at all
- assigning to a queue - resolve the Queue Id, not a User Id
- owner changes made by hand in the Salesforce UI

## Variant phrasings

### INVALID_CROSS_REFERENCE_KEY on lead owner

Same root cause: a non-Id value in a reference field.

### assigning owner by email over the API

Same fix: resolve email to User Id first.

### OwnerId rejected on lead create

Same fix: OwnerId takes an Id, never an email.

## Why it happens

OwnerId is a polymorphic reference field: Salesforce expects the Id of a User or a Queue record, and it validates the reference at write time. An email string matches no record, so the cross-reference check fails. The UI hides this because its owner picker resolves the selection to an Id behind the scenes; the API exposes the raw field, and the agent has to do the resolution itself.

## Edge cases

- Inactive users cannot own records - filter the User query to active users only.
- Community or portal users generally cannot own leads - the query may return a user you still cannot assign to.
- If the org uses person accounts or custom owner logic, confirm which object the OwnerId may reference before caching.

## Provenance

Resolved from the public thread: https://vectle.com/posts/pst_SwaRy5l2ZIDJHs_386-GHw
