*API REST › Referencia de endpoints › CRM › Contactos*

# Quitar la relación entre dos contactos

> Quita la relación entre dos contactos, en ambos sentidos, sea cual sea el orden en que se dé el par.

NO borra ninguno de los dos contactos. 404 si no estaban relacionados. Devuelve el contacto principal con detalle, la misma proyección que su GET, de modo que las `relatedContacts` restantes vuelven con él.

`DELETE /api/v1/contacts/{contactId}/related-contacts/{relatedContactId}` — permiso `contacts:write`

Permiso: `contacts:write` · 120/min · Ámbito heredado · Idempotency-Key · If-Match · MCP: `filmo_contacts_related_remove`

## Autorizaciones

Clave de API como Bearer, con el permiso de abajo. `contacts:write`

## Cabeceras

| Nombre | Tipo | Obligatorio | Descripción |
| --- | --- | --- | --- |
| `Idempotency-Key` | string | No | Identificador único de la petición, elegido por el cliente (sirve un UUID). Entre 1 y 255 caracteres: una cabecera vacía se rechaza con 400 `validation_error`, porque el cliente se creería protegido. Repetir la misma clave con el MISMO cuerpo en 24 h devuelve la respuesta guardada sin volver a ejecutar, con la cabecera `Idempotent-Replay: true`. Con un cuerpo DISTINTO devuelve 422. Es la forma segura de reintentar tras un tiempo de espera de red. |
| `If-Match` | string | No | Bloqueo optimista: el `ETag` que leíste del recurso (o `*`). Si el recurso ha cambiado desde entonces, la escritura se rechaza con `412 precondition_failed` antes de hacer nada; léelo de nuevo y reintenta. Sin la cabecera, la escritura siempre se realiza. |

## Parámetros de ruta

| Nombre | Tipo | Obligatorio | Descripción |
| --- | --- | --- | --- |
| `contactId` | string | Sí | Identificador del contacto principal. |
| `relatedContactId` | string | Sí | Identificador del contacto relacionado. |

## Ejemplos de petición

*Quitar la relación entre dos contactos*

**cURL**

```bash
curl https://app.filmo.io/api/v1/contacts/cont_8f2a1c/related-contacts/rela_8f2a1c \
  --request DELETE \
  --header "Authorization: Bearer $FILMO_API_KEY" \
  --header "Idempotency-Key: $(uuidgen 2>/dev/null || cat /proc/sys/kernel/random/uuid)"
```

**Node.js**

```javascript
fetch('https://app.filmo.io/api/v1/contacts/cont_8f2a1c/related-contacts/rela_8f2a1c', {
  method: 'DELETE',
  headers: {
    Authorization: 'Bearer YOUR_FILMO_API_KEY',
    'Idempotency-Key': crypto.randomUUID()
  }
})
```

**Python**

```python
import uuid
import requests

requests.delete(
    "https://app.filmo.io/api/v1/contacts/cont_8f2a1c/related-contacts/rela_8f2a1c",
    headers={
      "Authorization": "Bearer YOUR_FILMO_API_KEY",
      "Idempotency-Key": str(uuid.uuid4())
    }
)
```

## Respuesta

Esquema de la respuesta: `ContactDetailDto`

| Estado | Descripción |
| --- | --- |
| 200 | Respuesta correcta. |
| 401 | Fallo de autenticación (unauthorized, api_key_invalid, api_key_revoked, api_key_expired, api_key_owner_inactive, api_key_owner_not_admin). |
| 403 | No permitido (insufficient_scope: la clave de API no tiene el scope requerido, indicado en `requiredScope`; forbidden: el propietario de la clave carece del permiso que exige la operación). `insufficient_scope` se responde antes de que la petición cuente contra el límite de peticiones, así que no lleva cabeceras `RateLimit-*`; `forbidden` sí. |
| 404 | Recurso no encontrado (not_found): no existe o está fuera del alcance de la clave de API. |
| 409 | Conflicto (conflict): el estado actual del recurso impide la operación, ya hay en curso una petición con esa Idempotency-Key, o otra escritura condicional (`If-Match`) retuvo el recurso durante más de cinco segundos. `errors[0].code` indica cuál: `in_progress` y `lock_timeout` se resuelven reintentando; `invalid_state` no, porque el estado del recurso prohíbe la operación. |
| 412 | Precondición fallida (precondition_failed): la etiqueta de entidad `If-Match` no coincide con la representación actual. Lee el recurso de nuevo y reintenta. |
| 422 | Idempotency-Key reutilizada (idempotency_key_reused): la misma clave se usó antes con un cuerpo distinto. Cambia la clave. |
| 429 | Límite de peticiones superado (rate_limited): reintenta tras `Retry-After` segundos; `RateLimit-*` indica qué cuota se ha agotado. |
| 500 | Error interno (internal_error). |

**200**

| Nombre | Tipo | Descripción |
| --- | --- | --- |
| `data` | object | El contacto completo, tal como lo devuelve la API. |
| `data.id` | string | Identificador único del contacto. |
| `data.name` | string | Nombre del contacto (persona o empresa). |
| `data.contactType` | string | Tipo de contacto: person (particular) o company (negocio). `person`, `company` |
| `data.role` | string | Rol del contacto: client, provider, lead, debtor, creditor, student, teacher o unspecified. `client`, `provider`, `lead`, `debtor`, `creditor`, `student`, `teacher`, `unspecified` |
| `data.status` | string | Estado del contacto: active (operativo), inactive (desactivado) o archived (cerrado, conservado como histórico). `active`, `inactive`, `archived` |
| `data.nif` | string | NIF/CIF o número de identificación fiscal; null si no se ha registrado. |
| `data.commercialName` | string | Nombre comercial; null si coincide con el nombre o no consta. |
| `data.position` | string | Cargo o puesto de la persona de contacto; null si no está registrado. |
| `data.contactNumber` | string | Número de contacto asignado por la cuenta (referencia interna); null si no consta. |
| `data.emails` | object[] | Lista de direcciones de correo electrónico del contacto. |
| `data.emails.id` | string | Identificador interno del correo electrónico dentro del contacto. |
| `data.emails.label` | string | Etiqueta descriptiva del email; null si no tiene. |
| `data.emails.email` | string | Dirección de email. |
| `data.emails.isPrimary` | boolean | Si este es el email principal del contacto. |
| `data.billingEmail` | string | Email de facturación dedicado; null significa que se usa el email principal. |
| `data.phoneNumbers` | object[] | Lista de teléfonos del contacto. |
| `data.phoneNumbers.id` | string | Identificador interno del teléfono dentro del contacto. |
| `data.phoneNumbers.label` | string | Etiqueta descriptiva del teléfono; null si no tiene. |
| `data.phoneNumbers.number` | string | Número de teléfono. |
| `data.phoneNumbers.countryCode` | string | Prefijo telefónico internacional del país (p. ej. "+34"); null si no consta. |
| `data.phoneNumbers.isPrimary` | boolean | Si este es el teléfono principal del contacto. |
| `data.websites` | object[] | Lista de sitios web del contacto. |
| `data.websites.id` | string | Identificador interno del sitio web dentro del contacto. |
| `data.websites.label` | string | Etiqueta descriptiva del sitio web; null si no tiene. |
| `data.websites.url` | string | URL del sitio web. |
| `data.addresses` | object[] | Lista de direcciones del contacto. |
| `data.addresses.id` | string | Identificador interno de la dirección dentro del contacto. |
| `data.addresses.label` | string | Etiqueta descriptiva de la dirección; null si no tiene. |
| `data.addresses.addressLine1` | string | Primera línea de la dirección; null si no consta. |
| `data.addresses.addressLine2` | string | Segunda línea de la dirección; null si no consta. |
| `data.addresses.city` | string | Ciudad; null si no consta. |
| `data.addresses.province` | string | Provincia o región; null si no se ha registrado. |
| `data.addresses.postcode` | string | Código postal; null si no se ha registrado. |
| `data.addresses.country` | string | País; null si no consta. |
| `data.addresses.isMain` | boolean | Si esta es la dirección principal del contacto. |
| `data.folder` | string | Carpeta o agrupación organizativa; null si el contacto no está clasificado. |
| `data.discountPercentage` | number | Porcentaje de descuento general del contacto (0-100); null si no hay descuento configurado. |
| `data.preferredLanguage` | string | Idioma preferido del contacto; null si no se ha registrado. |
| `data.currency` | string | Moneda por defecto del contacto como código ISO 4217; null si no consta. |
| `data.paymentMethod` | object | Método de pago preferido del contacto; null si no está configurado. |
| `data.paymentMethod.type` | string | Tipo de método de pago: bank_transfer, card, cash, sepa_direct_debit u other. `bank_transfer`, `card`, `cash`, `other`, `sepa_direct_debit` |
| `data.paymentMethod.customValue` | string | Detalle en texto libre cuando el tipo es "other" (por ejemplo, un método registrado antes de configurarse); null en caso contrario. |
| `data.paymentDays` | integer[] | Días del mes en que el contacto hace pagos; null si no está configurado. |
| `data.expirationDays` | integer | Días de vencimiento por defecto de las facturas del contacto; null si no está configurado. |
| `data.subscribedToMarketing` | boolean | Si el contacto está suscrito a comunicaciones de marketing. |
| `data.isAppUser` | boolean | Si el contacto tiene acceso a la app. |
| `data.customFields` | object<string, any> | Valores de los campos personalizados por identificador de campo personalizado; las definiciones están en `/api/v1/custom-fields`. |
| `data.createdAt` | string<date-time> | Fecha de creación en ISO 8601 con desplazamiento. |
| `data.updatedAt` | string<date-time> | Fecha de la última actualización en ISO 8601 con desplazamiento. |
| `data.relatedContacts` | object[] | Contactos relacionados con este contacto (p. ej. las personas de una empresa). |
| `data.relatedContacts.id` | string | Identificador único del contacto relacionado. |
| `data.relatedContacts.name` | string | Nombre del contacto relacionado. |
| `data.relatedContacts.commercialName` | string | Nombre comercial del contacto relacionado; null si no consta. |
| `data.relatedContacts.contactType` | string | Tipo del contacto relacionado: person o company. `person`, `company` |
| `data.relatedContacts.role` | string | Rol del contacto relacionado. `client`, `provider`, `lead`, `debtor`, `creditor`, `student`, `teacher`, `unspecified` |
| `data.relatedContacts.status` | string | Estado del contacto relacionado: active, inactive o archived. `active`, `inactive`, `archived` |
| `data.relatedContacts.relationRole` | string | Rol de la relación entre los dos contactos (p. ej. "empleado de"); null si no consta. |
| `meta` | object | Información adicional sobre la respuesta. |
| `meta.warnings` | object[] | Avisos del ciclo de vida; solo aparecen cuando la operación está obsoleta. |
| `meta.warnings.code` | string | Código de aviso estable. `deprecated` |
| `meta.warnings.message` | string | Para personas: qué está obsoleto, hasta cuándo y qué usar. |
| `meta.warnings.sunset` | string | Día (YYYY-MM-DD) en que la operación deja de estar disponible. |
| `meta.warnings.replacement` | string | `operationId` de la operación que hay que usar en su lugar, o null si no hay ninguna. |
| `meta.warnings.link` | string | Dónde está documentada la deprecación (el changelog). |

*Respuesta de ejemplo · 200*

```json
{
  "data": {
    "id": "contact_9f3a2b",
    "name": "Example contact",
    "contactType": "person",
    "role": "client",
    "status": "active",
    "nif": "B00000000",
    "commercialName": "Example commercial name",
    "position": "Example position",
    "contactNumber": "2026-0001",
    "emails": [
      {
        "id": "email_9f3a2b",
        "label": "Main",
        "email": "ana.garcia@example.com",
        "isPrimary": true
      }
    ],
    "billingEmail": "ana.garcia@example.com",
    "phoneNumbers": [
      {
        "id": "phone_number_9f3a2b",
        "label": "Main",
        "number": "+34 600 000 000",
        "countryCode": "+34",
        "isPrimary": true
      }
    ],
    "websites": [
      {
        "id": "website_9f3a2b",
        "label": "Main",
        "url": "https://example.com/resource"
      }
    ],
    "addresses": [
      {
        "id": "address_9f3a2b",
        "label": "Main",
        "addressLine1": "Calle de Ejemplo 12",
        "addressLine2": "Calle de Ejemplo 12",
        "city": "Madrid",
        "province": "Madrid",
        "postcode": "28001",
        "country": "ES",
        "isMain": true
      }
    ],
    "folder": "Example folder",
    "discountPercentage": 1,
    "preferredLanguage": "es-ES",
    "currency": "EUR",
    "paymentMethod": {
      "type": "bank_transfer",
      "customValue": "Example custom value"
    },
    "paymentDays": [
      1
    ],
    "expirationDays": 1,
    "subscribedToMarketing": true,
    "isAppUser": true,
    "customFields": {
      "example": "example"
    },
    "createdAt": "2026-09-01T08:00:00.000Z",
    "updatedAt": "2026-09-01T08:00:00.000Z",
    "relatedContacts": [
      {
        "id": "related_contact_9f3a2b",
        "name": "Example related contact",
        "commercialName": "Example commercial name",
        "contactType": "person",
        "role": "client",
        "status": "active",
        "relationRole": "Example relation role"
      }
    ]
  }
}
```

## Errores

| Estado | Códigos | Significado |
| --- | --- | --- |
| 404 | `not_found` | No existe o queda fuera del alcance de la clave de API. |
| 409 | `conflict` | El estado actual del recurso impide la operación, ya hay en curso una petición con esa Idempotency-Key, u otra escritura condicional (`If-Match`) retuvo el recurso durante más de cinco segundos. `errors[0].code` indica cuál: `in_progress` y `lock_timeout` se resuelven reintentando; `invalid_state` no, porque el estado del recurso prohíbe la operación. |
| 412 | `precondition_failed` | La etiqueta de entidad `If-Match` no coincide con la representación actual. Vuelve a leer el recurso y reintenta. |
| 422 | `idempotency_key_reused` | La misma clave se usó antes con un cuerpo distinto. Cambia la clave. |

## Obsolescencia

Esta operación no está obsoleta. Las obsolescencias se anuncian con la cabecera Deprecation y una fecha de retirada.

---

URL canónica: https://docs.filmo.io/es/rest-api/endpoint-reference/contacts/unrelate-two-contacts  
Última actualización: 2026-10-25