Guardrails

Override prompt injection and DLP policy for one project

A project can override your organization’s prompt injection protection and data loss prevention policy. Use it when one workload needs a different posture from the rest of the organization: enforcement on a customer-facing assistant while an internal batch job stays in alert mode, or stricter DLP on the project that touches payroll data.

Overrides are sparse. A project sets only the fields it cares about and inherits everything else, so raising an organization threshold moves every project that did not pin that field. There is no way to express “inherit” with a full policy object, which is why omitting a field and sending it as null both mean inheritance here.

Authentication

These endpoints use manage_projects, the same scope as the rest of the Projects API, so any management key that can create a project can also set its guardrails. No re-mint needed.

A key acts only on its own organization, so a project belonging to another organization returns 404 rather than 403. A key without manage_projects returns 403.

How an override reaches a request

Whether a project may relax the organization policy depends on how the request names its project, not on how the override was written:

How the request names the projectWhat the override can do
The request is made with a project-scoped API keyTighten or relax, exactly as written
The request names the project itself, with the project_id body field or the X-Project-Id headerTighten only, anything that loosens the organization policy is ignored

The second row is a security boundary, not a limitation to work around. A caller that can name its own project could otherwise turn your protections off by asking. If you need a project to run a genuinely looser policy, give it a project-scoped API key.

A relaxation written for a project that is only ever named per request is silently inert. GET reports the policy as if the project were key-pinned, so it will show the relaxed value while requests keep running the organization policy.

Prompt injection

PUT /v1/projects/{project_id}/pi-settings replaces the override as a whole.

FieldDescriptionType
pi_modeDirect-injection mode: off, alert, or blockenum
pi_block_thresholdDirect-injection block threshold, 0 to 1. Must be at least the organization’s pi_pass_threshold.number
pi_indirect_modeIndirect-injection mode, a separate axis from pi_mode: off, alert, or blockenum
pi_tier2a_block_thresholdIndirect-injection heuristic-confidence threshold, 0 to 1number
pi_tier2b_block_thresholdIndirect-injection embedding-similarity threshold, 0 to 1number
pi_output_actionWhat to do when the response-side check fires: observe, redact, route, block, or escalateenum
pi_fail_closedWhether to reject requests when detection is unavailable. Defaults to failing open.boolean
pi_allowlist_patternsRegexes whose matching segments skip scanning, max 20list of strings

pi_pass_threshold, pi_input_action, pi_safer_vendor_route, and pi_log_full_text_on_block stay at the organization level and are rejected with a 422 here. The last of those is gated by your data processing agreement, so its boundary is deliberately the organization.

$curl -X PUT https://api-gateway.merge.dev/v1/projects/{project_id}/pi-settings \
> -H "Authorization: Bearer mgmt_<your_management_key>" \
> -H "Content-Type: application/json" \
> -d '{
> "pi_indirect_mode": "block",
> "pi_tier2a_block_threshold": 0.6
> }'

The response carries both layers, so you never merge them yourself:

1{
2 "override": { "pi_indirect_mode": "block", "pi_tier2a_block_threshold": 0.6, "pi_mode": null },
3 "effective": { "pi_mode": "alert", "pi_indirect_mode": "block", "pi_tier2a_block_threshold": 0.6 },
4 "inherited_fields": ["pi_mode", "pi_block_threshold", "pi_output_action"]
5}

pi_allowlist_patterns is unioned with the organization’s list rather than replacing it, so a project cannot drop an exception the organization curated.

An allowlist pattern is a regex matched against each segment, and a matching segment is skipped before it is ever scored. A pattern broad enough to match ordinary prose, s for example, does not narrow enforcement, it switches the axis off while the mode still reads block. Anchor patterns to something specific to your traffic.

Data loss prevention

PUT /v1/projects/{project_id}/dlp-settings replaces the override as a whole. The payload is keyed by entity type under override:

$curl -X PUT https://api-gateway.merge.dev/v1/projects/{project_id}/dlp-settings \
> -H "Authorization: Bearer mgmt_<your_management_key>" \
> -H "Content-Type: application/json" \
> -d '{
> "override": {
> "US_SSN": { "action": "block" },
> "EMAIL_ADDRESS": { "enabled": false }
> }
> }'
FieldDescriptionType
enabledWhether this entity is scanned for this projectboolean
actionWhat to do on a match: log, redact, or blockenum

Both fields are optional, so {"action": "block"} changes the action and inherits whether the entity is scanned at all. The entity must already exist for your organization, either as a seeded rule or a custom one, otherwise the write is rejected with a 422 naming the unknown types. That way a typo does nothing rather than silently nothing.

The response returns your override plus every entity in the organization’s catalog with the merge applied:

1{
2 "override": { "US_SSN": { "action": "block", "enabled": null } },
3 "entities": [
4 {
5 "entity_type": "US_SSN",
6 "category": "USA",
7 "is_seeded": true,
8 "org_action": "redact",
9 "effective_action": "block",
10 "overridden": true
11 }
12 ]
13}

An effective_action of null means the entity is not scanned for this project.

Inheriting again

DELETE on either resource clears the override so the project inherits the organization policy as a whole. PUT with an empty body ({} for PI, {"override": {}} for DLP) does the same thing. To drop one field while keeping the rest, send that field as null.

$curl -X DELETE https://api-gateway.merge.dev/v1/projects/{project_id}/pi-settings \
> -H "Authorization: Bearer mgmt_<your_management_key>"

Limits

Overrides ride along with your organization’s settings on every request, so they are bounded at write time. A write past a limit is rejected with a 422 that names which one.

LimitValue
Projects with a PI override, per organization100
Allowlist patterns per project20
Projects with a DLP override, per organization50

The DLP limit is also bounded by total size, because a shipped policy repeats every custom recognizer you have defined. An organization with many large custom rules can hit the size limit well before 50 projects.

Full endpoint contracts live in the Management API reference.