Clay Subroutine Allowlist

Lets the agent run Clay's built-in enrichment functions (Work Email, Person Job Title, Enrich Company, Tech Stack and the rest) and blocks every other Clay…

On this page
  1. What it does
  2. Compliance alignment
  3. Why ingress
  4. How it matches
  5. Examples
  6. Allowed (Work Email on given inputs)
  7. Denied (custom function)
  8. Composition
  9. Known limitations
  10. Policy source (Rego)
  11. Related policies
Direction
ingress (tool_pre_invoke)
Default
deny on match, allow otherwise
Rego package
clay.ingress.subroutine_allowlist
App
Clay
Bundles
Gtm Stack HubspotGtm Stack Salesforce
Published
Minimum gateway
1.0.0b24
Schema version
1.0.0
Checksum
sha256:d29e5e639097b2b543542ea7a3cc908bb9ba806e5d9d1c7b9848dd8303e85f73

What it does

Lets the agent run Clay's built-in enrichment functions (Work Email, Person Job Title, Enrich Company, Tech Stack and the rest) and blocks every other Clay function.

Clay's run-subroutine tools run any function in the workspace, not just enrichment. A workspace can hold custom functions that push data out — add contacts to a Salesforce campaign, update a HubSpot record, send a webhook. Those writes come from Clay, so the CRM policies on the gateway never see them. This policy closes that path: only functions on the allowlist run.

  • Search and data-point tools pass. search-*, query-objects, add-company-data-points, add-contact-data-points, get-task* and the rest are untouched.
  • Subroutine calls are checked. run-subroutine, run-subroutine-direct and run-subroutine-no-mapping are allowed only when subroutine_id is on the allowlist. A missing or unknown ID is denied.

Compliance alignment

  • SOC 2 CC6.1; CC6.3 — least privilege: the agent gets enrichment, not arbitrary workspace automation.
  • SOC 2 CC8.1 — change management: CRM changes can't be made through a side channel that bypasses the CRM's own controls.
  • ISO 27001 A.5.15 — access control: enforces which Clay functions the agent channel can invoke.

Why ingress

A custom function's side effects (CRM writes, outbound webhooks) happen inside Clay once it runs. The function is fully identified by subroutine_id in the request, so denying at ingress stops it before it starts.

How it matches

A tool is in scope when its (lowercased) name starts with clay and contains run-subroutine or run_subroutine. For those calls, payload.args.subroutine_id must be one of the IDs in _allowed_ids.

The default list holds the 19 built-in enrichment functions as Clay returned them from list-subroutines on 2026-10-03: Company Industry, Company Domain, Company Address, Company Employee Count, Company Latest Funding, Company News, Company Revenue (Exact), Enrich Company, Company Job Openings, Person Full Name, Enrich Person, Find People at Company, Mobile Phone Number, Enrich Person and Find Contact Details, Person Location, Website Traffic, Website Technology Stack, Person Job Title, Work Email.

Check the IDs against your workspace before deploying. Call list-subroutines through the gateway and compare. If your IDs differ, replace the list; until you do, the policy fails closed and blocks those functions with a clear reason, rather than letting anything through.

Examples

Allowed (Work Email on given inputs)

{
  "input": {
    "action": "tool_pre_invoke",
    "resource": { "name": "clay-run-subroutine-direct", "type": "tool" },
    "payload": {
      "args": {
        "subroutine_id": "t_0tm6n9mtip5BqhnMv5d",
        "inputs": [ { "Full Name": "Jane Doe", "Company Domain": "acme.com" } ]
      }
    }
  }
}

allow = true, no reason.

Denied (custom function)

{
  "input": {
    "action": "tool_pre_invoke",
    "resource": { "name": "clay-run-subroutine", "type": "tool" },
    "payload": {
      "args": {
        "subroutine_id": "t_custom_push_to_sfdc",
        "taskId": "mcp-task-123",
        "inputs": { "Salesforce Campaign ID": "7018b00000ABCDEF" }
      }
    }
  }
}

allow = false, reason = "Blocked: only Clay's built-in enrichment functions can run through this gateway. Custom Clay functions can push data into other systems, so ask your admin to add this one to the allowlist if you need it.".

Composition

Pairs with the CRM write posture in the same bundle. The CRM policies control what the agent can change in Salesforce or HubSpot directly; this one stops the agent from changing it indirectly through Clay. Volume and credit spend are a separate concern — cap them with a separate policy.

Known limitations

  • IDs are workspace data. The default list comes from one Clay workspace. Confirm yours with list-subroutines.
  • Contact details are allowed. Mobile Phone Number, Work Email and Enrich Person and Find Contact Details return personal contact data. Remove their IDs if the agent shouldn't fetch them, or mask the values with an egress redaction policy.
  • Prefix scope. Only tools whose name starts with clay are in scope. Change the prefix if your gateway registers the server under another name.
  • No identity-based exemptions. To let RevOps admins run custom functions, add an exception gated on input.subject.claims.

Compliance note. This policy supports alignment with the cited framework controls on the MCP path only. No policy or bundle makes an organization compliant with any framework; web-UI, native-API, and in-app access are outside the gateway's reach by design. Validate against your own compliance program before relying on it.

Policy source (Rego)

package clay.ingress.subroutine_allowlist

import future.keywords.if
import future.keywords.in

# Allow broadly; deny only Clay subroutine runs that aren't on the list.
default allow := true

# Clay's built-in enrichment functions. Confirm against `list-subroutines`
# in your workspace and replace if the IDs differ.
_allowed_ids := {
    "t_0tm6n9dEsVBMcS7oBvc", # Company Industry
    "t_0tm6n9dFNTJjpVjZaYT", # Company Domain
    "t_0tm6n9dG96YSDVGRXfM", # Company Address
    "t_0tm6n9dVqh5MuXB9EXk", # Company Employee Count
    "t_0tm6n9hMMAfCCgSJwgV", # Company Latest Funding
    "t_0tm6n9hpQ5EmoAreRds", # Company News
    "t_0tm6n9hWiPpnUCkZMax", # Company Revenue (Exact)
    "t_0tm6n9hxVZpR4Cy6eYh", # Enrich Company
    "t_0tm6n9hirHaxjjwgksd", # Company Job Openings
    "t_0tm6n9iAu7KzcRycseU", # Person Full Name
    "t_0tm6n9i5AtDR7HatCyJ", # Enrich Person
    "t_0tm6n9iWQEKDYvceygd", # Find People at Company
    "t_0tm6n9imvUTzmEhq997", # Mobile Phone Number
    "t_0tm6n9ifsSooEgeeMCh", # Enrich Person and Find Contact Details
    "t_0tm6n9miExJo3rhcgMf", # Person Location
    "t_0tm6n9mo9Xk2gRm8r6W", # Website Traffic
    "t_0tm6n9mKSfSitNJRCbw", # Website Technology Stack
    "t_0tm6n9mkCFSdffP4HwB", # Person Job Title
    "t_0tm6n9mtip5BqhnMv5d", # Work Email
}

_tool := lower(input.resource.name)

_is_subroutine_call if {
    startswith(_tool, "clay")
    regex.match(`run[-_]subroutine`, _tool)
}

_subroutine_id := object.get(object.get(input.payload, "args", {}), "subroutine_id", "")

_is_allowed_subroutine if {
    is_string(_subroutine_id)
    _subroutine_id in _allowed_ids
}

allow := false if {
    _is_subroutine_call
    not _is_allowed_subroutine
}

reason := "Blocked: only Clay's built-in enrichment functions can run through this gateway. Custom Clay functions can push data into other systems, so ask your admin to add this one to the allowlist if you need it." if {
    _is_subroutine_call
    not _is_allowed_subroutine
}

Canonical source: policy.md on GitHub · raw · raw on this site (.md)

↑↓ to navigate ↵ to open

Get in touch

Tell us what you need to govern.

A few lines is plenty — what you're deploying, and what you need control over. We read every one.