---
title: "Upsert an array of partner-managed-account relationships"
method: POST
path: "/portfolio_relationships/batch"
tags: ["Portfolio Relationships"]
---

# Upsert an array of partner-managed-account relationships

`POST /portfolio_relationships/batch`

## Request

- Base URL: `https://api.lassotech.com/api/v1/rest`
- URL: `https://api.lassotech.com/api/v1/rest/portfolio_relationships/batch`
- Auth: API key in header `x-lasso-access-token`

Upserts an array of relationships in one request. Best-effort: valid rows commit independently;
a bad row returns an error code without blocking the others in the same batch. Each result
carries the `index` of its row. The same customer appearing twice in one batch fails both rows
with `duplicate_customer_in_batch`. Requires `portfolio_write` permission.

## Request body

- object
  - `relationships` PortfolioRelationshipRow[], required
    - `partner` PortfolioRelationshipPartnerLookup, required: Find-or-fail partner identifier. Provide one field.
      - `lasso_partner_key` string: The partner's Lasso Partner Key (falls back to matching the slug)
      - `crm_account_id` string: An external ID already linked to the partner (Salesforce Account / HubSpot Company / Pipedrive Organization)
    - `customer` PortfolioRelationshipCustomerLookup, required: Find-or-create customer identifier. A create requires crm_account_id, external_id, or domain. Identifiers are recorded on every push, not just on create: an empty slot is filled from the values you send, while a populated slot, or an identifier another customer already holds, is left alone without failing the row.
      - `lasso_customer_key` string: An existing customer's Lasso key (find-only, never creates)
      - `crm_account_id` string: Your CRM's identifier for this customer. Stored as crm_record_id and also recorded as an external ID link on the customer (Salesforce account / HubSpot company / Pipedrive organization). Repeat pushes do not add a second link.
      - `crm_system` 'SALESFORCE' | 'HUBSPOT' | 'PIPEDRIVE': Which CRM crm_account_id came from. Optional; inferred from the account's CRM connector when omitted. Required to create a link on an account with more than one CRM connected. An unrecognized value fails the row with validation_failed.
      - `external_id` string: Your own external identifier for this customer. Use a key that is stable for the life of the customer: this is their identity, so changing it resolves to a different customer and creates a duplicate.
      - `domain` string: Customer's domain, normalized; personal email domains are never used as an identity key
      - `name` string: Customer display name, only written on create
    - `engagement_type` string, nullable: Key of an engagement type already configured for this account. Omitting the field leaves the current engagement type unchanged; sending an explicit null or an empty string clears it.
    - `started_at` string, date-time: When the relationship began. Defaults to now.
    - `status` string: Set to "inactive" to immediately expire the relationship. Any other value (or omitted) keeps it active.
    - `valid_until` string, date-time: Hard expiry, takes precedence over heartbeat_ttl_days and clears it.
    - `heartbeat_ttl_days` integer: Soft expiry in days. Defaults to the account's configured heartbeat window if neither this nor valid_until is provided.
    - `metrics` PortfolioMetric[]
      - `key` string, required: Must match a metric key already configured for this account
      - `value` number, required
      - `currency` string: ISO currency code, required if the metric's configured unit type is currency
      - `as_of` string, date-time: Defaults to request time
  - `idempotency_key` string: Replaying the same key within 24 hours returns the original response without processing the batch again. Only a batch in which at least one row succeeded is remembered, so a batch that failed entirely can be fixed and resent under the same key.
  - `as_of` string, date-time: Batch-level default for any metric that doesn't specify its own as_of. An invalid value fails the whole request with `422`.

## Response `200`

Batch processed (see per-row results for individual outcomes)

- object
  - `processed` integer
  - `succeeded` integer
  - `failed` integer
  - `results` union[]
    - union
      - PortfolioRelationshipResult
        - `index` integer: Position of this row in the batch request. Present on batch results only.
        - `status` 'created' | 'updated'
        - `customer_id` string: The customer's Lasso key
        - `customer_created` boolean
        - `relationship_id` integer
        - `warnings` object[]: Present only when part of the row was not applied although the row succeeded. `identifier_not_recorded`: a customer identifier you sent was not stored, because the customer already holds a different value, another customer already holds that one, or (for `domain`) it is a personal-email domain or not a usable domain. `crm_link_skipped`: no CRM link was created for `crm_account_id`, because the customer is already linked to a different record in the same CRM.
          - `code` 'identifier_not_recorded' | 'crm_link_skipped'
          - `detail` string
      - PortfolioRelationshipRowError
        - `index` integer: Position of this row in the batch request
        - `status` 'error'
        - `error` 'partner_not_found' | 'customer_identifier_missing' | 'engagement_type_not_found' | 'invalid_metric_value' | 'currency_required' | 'invalid_currency' | 'invalid_date' | 'invalid_ttl' | 'unknown_metric_key' | 'unknown_field' | 'duplicate_customer_in_batch' | 'customer_identifier_conflict' | 'validation_failed'
        - `detail` string, nullable

## Other responses

- `401`: Missing or invalid API key
- `403`: API key lacks required permission
- `422`: The whole request was refused and no row was processed. A missing or non-array `relationships` returns `{"errors": [{"field": "relationships", "message": "REQUIRED"}]}`; an invalid batch-level `as_of` returns `{"error": "invalid_date", "detail": "..."}`.
- `429`: Rate limit exceeded. Wait the number of seconds in `Retry-After`, then retry.

---

[API](https://skmtc.dev/onelasso/apis/lasso-rest-api.md) · [All operations](https://skmtc.dev/onelasso/apis/lasso-rest-api/llms.txt) · [OpenAPI document](https://skmtc.dev/onelasso/apis/lasso-rest-api/revisions/bbc328f5e2d1?raw)
