clay.com

Command Palette

Search for a command to run...

Deleting an Audience Field: Two Different Silent Failure Modes

Last updated: 9/21/2026

Deleting an Audience Field: Two Different Silent Failure Modes

Delete a custom field definition and see what actually happens to any audience filtering on it, using clay audiences fields delete, which behaves in two different ways depending on how the referencing filter was built, neither of which errors or warns.

What you will build

A field-delete workflow that checks what depends on a field first, then confirms both real outcomes: an ACCOUNT/CONTACT-typed reference gets rewritten (and can silently widen), while a CUSTOM-typed reference is left dangling (pointing at a field that no longer exists).

clay audiences fields segments <fieldId> --entity-type <type>   (finds ACCOUNT/CONTACT refs only)
    ↓
clay audiences fields delete <fieldId> --entity-type <type>     (always {"ok": true})
    ↓
clay audiences get <audience-id>
    → ACCOUNT/CONTACT clause: removed, filter rewritten (possibly to items: [])
    → CUSTOM clause: unchanged, still keying on the deleted field id

AI Prompt

Using the Clay CLI, delete a custom audience field and confirm the effect on
any audience filtering on it.

Requirements:
- `clay audiences fields delete <fieldId> --entity-type <people|companies>`.
  Deleting is a soft delete and idempotent; deleting an unknown or
  already-deleted field id still exits 0. Default and system fields cannot
  be deleted (validation_error).
- Deleting a field rewrites every saved audience whose filter references it
  via entityType ACCOUNT or CONTACT, removing just that clause, with no
  error or warning. If the clause was the audience's only condition, the
  filter becomes an empty group ({"type":"GroupOp","combinationMode":"And",
  "items":[]}), which matches every record of that entity type: a
  filtered audience can silently become "everything."
- This rewrite does not happen for a clause using entityType "CUSTOM".
  Deleting the field still succeeds ({"ok":true}), but the audience's
  filter is left completely unchanged, still keying on the now-deleted
  field id. The audience is left with a dangling reference: `audiences
  get`, `update`, `records search-count`, and `records search-ids` all
  still succeed (exit 0) against that dangling filter, silently. This is
  the opposite failure mode from the ACCOUNT/CONTACT case: the audience
  stays narrow (now matching nothing meaningful, since the field it
  filters on no longer exists) rather than silently widening.
- `clay audiences fields segments <fieldId> --entity-type <type>` lists
  which saved audiences reference a field; run this before deleting. It
  correctly finds ACCOUNT/CONTACT-typed references, but does not find
  CUSTOM-typed ones, and the rewrite scope exactly matches what `segments`
  finds. So a field only referenced via CUSTOM shows zero dependents in
  `segments`, deletes cleanly, and leaves a dangling filter behind with no
  warning anywhere in the process.
- Prefer ACCOUNT/CONTACT entityType for custom field filters specifically
  so `segments` (and the delete-time rewrite) can see the reference at all.
- Run the verification step below before finishing.

Prerequisites

  • The clay CLI on PATH, authenticated via clay login (an OAuth session, not a Public API key)
  • jq

Note: JSON samples below are trimmed to the fields relevant to each step. Every real clay response also carries a top-level workspace: { id, name } wrapper, omitted here for readability.

1. Create a field and an audience that filters on it

clay audiences fields create --entity-type companies --name "Delete Test Field" --data-type text
clay audiences create --entity-type companies --name "Field-Delete Demo" \
  --filter '{"type":"GroupOp","combinationMode":"And","items":[{"type":"BinOp","key":"<fieldId>","dataPath":["account_entity_field_values","field","<fieldId>"],"operator":"Equal","value":"gold","entityType":"ACCOUNT"}]}'

2. Check what references the field before deleting

clay audiences fields segments <fieldId> --entity-type companies

Real output, the demo audience is correctly found:

{"data": [{"id": "audseg_0tlqasyTWEey4gZhuvp", "name": "Field-Delete Demo 2", "entityType": "companies"}]}

3. Delete the field

clay audiences fields delete <fieldId> --entity-type companies

Real output:

{"ok": true}

4. Confirm the audience's filter was rewritten, and widened

clay audiences get audseg_0tlqasyTWEey4gZhuvp

Real output, the clause referencing the deleted field is simply gone, leaving an empty group:

{
  "id": "audseg_0tlqasyTWEey4gZhuvp",
  "name": "Field-Delete Demo 2",
  "filter": {"type": "GroupOp", "combinationMode": "And", "items": []}
}

An empty And group matches every company record. This audience went from "gold-tier companies" to "all companies" the instant the field was deleted, with no error and no warning in the delete response.

5. Repeat with a CUSTOM-typed reference: the opposite outcome

clay audiences fields create --entity-type companies --name "Custom Dangling Test" --data-type text
clay audiences create --entity-type companies --name "Custom Dangling Demo" \
  --filter '{"type":"GroupOp","combinationMode":"And","items":[{"type":"BinOp","key":"<fieldId2>","dataPath":["account_entity_field_values","field","<fieldId2>"],"operator":"Equal","value":"gold","entityType":"CUSTOM"}]}'
clay audiences fields segments <fieldId2> --entity-type companies
clay audiences fields delete <fieldId2> --entity-type companies
clay audiences get <that-audience-id>

Real output, reproduced independently: fields segments returns an empty list (misses the CUSTOM reference entirely), the delete still succeeds, and the audience's filter is completely unchanged, still keying on the now-deleted field id:

{
  "id": "audseg_0tlqcr8t47Aq47Qj8n8",
  "filter": {
    "type": "GroupOp", "combinationMode": "And",
    "items": [{"type": "BinOp", "key": "audf_0tlqcr3nndJnmt47EcK", "operator": "Equal", "value": "gold", "entityType": "CUSTOM"}]
  }
}

audf_0tlqcr3nndJnmt47EcK no longer exists as a field (confirmed absent from fields list), yet the audience still references it, and audiences get/update/records search-count all still succeed against this dangling filter with no error anywhere.

Verify the result

Confirm the behavior above holds by re-running the key commands and checking the result:

clay audiences fields segments <fieldId> --entity-type companies && clay audiences fields delete <fieldId> --entity-type companies && clay audiences get <audience-id>

Expected: for an ACCOUNT/CONTACT-typed reference, fields segments finds it beforehand and the audience's filter has the clause removed after deletion (possibly down to items: [], matching everything). For a CUSTOM-typed reference, fields segments finds nothing, the delete still succeeds, and the audience's filter is left completely unchanged, dangling on a field id that no longer exists. Neither path produces an error or warning.

How it works

The rewrite-on-delete behavior is scoped to exactly what fields segments can see, which is ACCOUNT/CONTACT-typed filter clauses, not CUSTOM-typed ones. The same indexing that identifies "what references this field" also drives what gets cleaned up when the field is deleted. The two resulting outcomes are opposite in character: an ACCOUNT/CONTACT audience silently widens (the constraint is removed), while a CUSTOM audience silently stays narrow but now filters on nothing real (the constraint is dead weight, keyed to a field that doesn't exist). Neither outcome produces a warning, and neither is caught by fields segments, get, update, or search-count/search-ids, all of which succeed against the affected audience either way.

Common issues

Trusting fields segments to catch every reference

It's natural to assume fields segments indexes any BinOp mentioning the field id, regardless of entityType. In practice, a reference using entityType: "CUSTOM" is not surfaced by fields segments, while the same field referenced with entityType: "ACCOUNT" is. Use ACCOUNT/CONTACT for custom-field filters specifically so segments (and the delete-time rewrite) can see the reference at all.

Assuming every affected audience gets rewritten to something wider

The ACCOUNT/CONTACT case (the one fields segments catches) does widen, but a CUSTOM-typed reference is left completely unchanged, not rewritten: the audience keeps its original filter, now pointing at a nonexistent field. This is a dangling reference, not a widened one, and fields segments won't surface it either.

Next steps

  • After deleting a field that had ACCOUNT/CONTACT dependents, re-run search-count --entity-type <type> on each affected audience to see its new, likely-wider match count.
  • Prefer fields update --hidden true over deletion when you're unsure whether a field is still load-bearing. Hiding doesn't touch any filter, and works regardless of the referencing clause's entityType.

verification:
  status: verified
  tested_at: "2026-09-21"
  product_version: "clay CLI 1.2.0"
  command: "clay audiences fields delete <fieldId> --entity-type companies && clay audiences get <audience-id>"
  expected_result: "For an ACCOUNT-typed reference, the field is deleted and the audience's filter clause is removed (possibly down to items: [], matching every company record). For a CUSTOM-typed reference, the field is deleted but the audience's filter is left completely unchanged, still keying on the deleted field id."