Enterprise Integration Guide

Integration

How to integrate PillarShield's content governance API into custom applications, enterprise CMS platforms (Adobe AEM, Sitecore, Contentful), or CI/CD pipelines.

Overview

PillarShield provides a synchronous REST API that evaluates content against your tenant's governance rules — PII detection, prohibited terms, tone analysis, and moderation — and returns a compliance decision. It sits at the save or publish boundary of your content lifecycle.

Key Capabilities

  • Real-time compliance: ~1.2 seconds for a full governance check including AI analysis.
  • Deterministic rules: Regex-based PII, credential, and prohibited term blocking. Same input, same result, every time.
  • AI checks: Tone scoring, PII classification, and OpenAI-powered moderation. Warns below your certainty threshold, blocks above it.
  • Audit trail: Every check is logged server-side as immutable evidence.

Quick Start

Base URL: https://api.pillarshield.co/pillarshield-governance

Authentication: API key (psk_ prefix) in the JSON request body. Not an HTTP header.

API Reference: pillarshield.co/docs/api (OpenAPI spec)

Security: Never expose your api_key in client-side code. Always call the API from your backend or CI environment.

Minimal Example (curl)

The simplest possible governance check. Two required fields: api_key and content.

curl -X POST https://api.pillarshield.co/pillarshield-governance/governance \
  -H "Content-Type: application/json" \
  -d '{
    "api_key": "psk_YOUR_KEY",
    "content": "Call John at 555-867-5309 to discuss the product launch."
  }'

Response:

{
  "status": "VIOLATION_DETECTED",
  "is_compliant": false,
  "reason": ["PII detected: phone number"],
  "violations": [
    {
      "type": "PII_PHONE",
      "severity": "error",
      "effective_severity": "error",
      "message": "PII detected: phone number",
      "location": { "start": 13, "end": 25 },
      "match": {
        "kind": "phone",
        "value": "555-***-****",
        "masked": true
      }
    }
  ],
  "request_id": "4f1f0b77-8c5c-4b2a-9b25-0b3a2f5f2f33"
}

That's it. Check is_compliant. If false, the violations array tells you what was found and where.

Integration Patterns

1. Pre-Save Hook (Recommended)

Block non-compliant content before it reaches your database. This is what the Drupal and WordPress connectors do natively.

// Node.js — Express middleware example
const axios = require('axios');

const PILLARSHIELD_URL = 'https://api.pillarshield.co/pillarshield-governance/governance';

async function governanceCheck(content, pageContext) {
  const { data } = await axios.post(PILLARSHIELD_URL, {
    api_key: process.env.PILLARSHIELD_API_KEY,
    content: content,
    context: {
      page_type: pageContext.type,
      page_title: pageContext.title,
      content_context: 'ui:publish'
    }
  });
  return data;
}

// In your save handler:
app.post('/api/content', async (req, res) => {
  const result = await governanceCheck(req.body.content, {
    type: 'blog',
    title: req.body.title
  });

  if (!result.is_compliant) {
    return res.status(422).json({
      error: 'Content governance violation',
      violations: result.violations.map(v => v.message)
    });
  }

  await db.save(req.body);
  res.json({ saved: true });
});

2. Python (Django/Flask)

Same pattern, different language.

import os
import requests

PILLARSHIELD_URL = "https://api.pillarshield.co/pillarshield-governance/governance"

def check_content(content, page_type="page", page_title=""):
    response = requests.post(PILLARSHIELD_URL, json={
        "api_key": os.environ["PILLARSHIELD_API_KEY"],
        "content": content,
        "context": {
            "page_type": page_type,
            "page_title": page_title,
            "content_context": "ui:publish"
        }
    })
    return response.json()

# Usage:
result = check_content(draft_body, page_type="blog", page_title="New Post")
if not result["is_compliant"]:
    errors = [v["message"] for v in result["violations"]]
    raise ValidationError(f"Blocked: {', '.join(errors)}")

3. CI/CD Pipeline

For teams managing content as code (Markdown docs, static site generators), enforce governance in your build pipeline.

# GitHub Actions example
name: Content Governance
on: [pull_request]

jobs:
  governance:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0

      - name: Check changed content files
        env:
          PILLARSHIELD_API_KEY: ${{ secrets.PILLARSHIELD_API_KEY }}
        run: |
          # Find changed .md files in this PR
          FILES=$(git diff --name-only origin/main -- '*.md')

          FAILED=0
          for f in $FILES; do
            echo "Checking: $f"
            CONTENT=$(cat "$f")

            RESPONSE=$(curl -s -X POST \
              https://api.pillarshield.co/pillarshield-governance/governance \
              -H "Content-Type: application/json" \
              -d "$(jq -n \
                --arg key "$PILLARSHIELD_API_KEY" \
                --arg content "$CONTENT" \
                --arg title "$(basename "$f" .md)" \
                '{api_key: $key, content: $content, context: {page_type: "markdown", page_title: $title, content_context: "ui:publish"}}')")

            COMPLIANT=$(echo "$RESPONSE" | jq -r '.is_compliant')

            if [ "$COMPLIANT" = "false" ]; then
              echo "❌ BLOCKED: $f"
              echo "$RESPONSE" | jq '.violations[] | .message'
              FAILED=1
            else
              echo "✅ Passed: $f"
            fi
          done

          if [ "$FAILED" -eq 1 ]; then
            echo "::error::Content governance violations found. Fix and re-push."
            exit 1
          fi

Deep Dive: Git Pre-Push Hook

Some content teams publish directly from Git — merging to main triggers a deploy. If that's your workflow, you can enforce governance with a Git pre-push hook that blocks the push if any changed content files have violations.

This is a local hook. It runs on the developer's machine before the push reaches the remote. Nothing ships if content doesn't pass.

How it works

  1. Developer finishes editing content files (.md, .html, .json)
  2. Developer runs git push
  3. The pre-push hook identifies changed content files vs. the remote
  4. Each file is sent to POST /governance
  5. If any file fails: push is blocked, violations are printed
  6. If all pass: push proceeds normally

Install

Save the script below as .git/hooks/pre-push and make it executable (chmod +x .git/hooks/pre-push). For team-wide enforcement, commit the hook to your repo and have developers symlink it, or use a tool like Lefthook or Husky to manage hooks automatically.

The hook

#!/usr/bin/env bash
#
# .git/hooks/pre-push
# PillarShield governance check on changed content files before push.
#
# Requirements: curl, jq
# Environment:  PILLARSHIELD_API_KEY must be set
#
# Content file patterns to check (edit to match your repo):
CONTENT_PATTERNS="*.md *.html *.json"

set -euo pipefail

API_URL="https://api.pillarshield.co/pillarshield-governance/governance"

if [ -z "${PILLARSHIELD_API_KEY:-}" ]; then
  echo "⚠️  PILLARSHIELD_API_KEY not set. Skipping governance check."
  echo "   Set it in your shell profile or .env to enable pre-push checks."
  exit 0
fi

# Read the remote and URL that git is pushing to
read -r REMOTE_NAME REMOTE_URL

# Determine what's being pushed
while read -r LOCAL_REF LOCAL_SHA REMOTE_REF REMOTE_SHA; do
  # Skip delete pushes
  if [ "$LOCAL_SHA" = "0000000000000000000000000000000000000000" ]; then
    continue
  fi

  # If the remote branch doesn't exist yet, compare against main
  if [ "$REMOTE_SHA" = "0000000000000000000000000000000000000000" ]; then
    REMOTE_SHA="origin/main"
  fi

  # Get changed content files
  FILES=""
  for PATTERN in $CONTENT_PATTERNS; do
    MATCHED=$(git diff --name-only "$REMOTE_SHA" "$LOCAL_SHA" -- "$PATTERN" 2>/dev/null || true)
    FILES="$FILES $MATCHED"
  done
  FILES=$(echo "$FILES" | xargs)  # trim whitespace

  if [ -z "$FILES" ]; then
    echo "✅ No content files changed. Push proceeding."
    exit 0
  fi

  echo "🛡️  PillarShield: checking $(echo "$FILES" | wc -w | xargs) content file(s)..."
  echo ""

  FAILED=0
  CHECKED=0
  BLOCKED=0

  for FILE in $FILES; do
    # Skip deleted files
    if [ ! -f "$FILE" ]; then
      continue
    fi

    CHECKED=$((CHECKED + 1))
    CONTENT=$(cat "$FILE")
    TITLE=$(basename "$FILE" | sed 's/\.[^.]*$//')

    RESPONSE=$(curl -s -X POST "$API_URL" \
      -H "Content-Type: application/json" \
      -d "$(jq -n \
        --arg key "$PILLARSHIELD_API_KEY" \
        --arg content "$CONTENT" \
        --arg title "$TITLE" \
        '{
          api_key: $key,
          content: $content,
          context: {
            page_type: "markdown",
            page_title: $title,
            content_context: "ui:publish"
          }
        }')")

    STATUS=$(echo "$RESPONSE" | jq -r '.status')
    COMPLIANT=$(echo "$RESPONSE" | jq -r '.is_compliant')
    REQUEST_ID=$(echo "$RESPONSE" | jq -r '.request_id // "N/A"')

    if [ "$COMPLIANT" = "false" ]; then
      BLOCKED=$((BLOCKED + 1))
      FAILED=1
      echo "❌ BLOCKED: $FILE"
      echo "   Status: $STATUS | Request ID: $REQUEST_ID"
      echo "$RESPONSE" | jq -r '.violations[] | "   → [\(.type)] \(.message)"'
      echo ""
    else
      echo "✅ Passed:  $FILE"
    fi
  done

  echo ""
  echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
  echo "  Checked: $CHECKED  |  Passed: $((CHECKED - BLOCKED))  |  Blocked: $BLOCKED"
  echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"

  if [ "$FAILED" -eq 1 ]; then
    echo ""
    echo "🚫 Push blocked. Fix the violations above and try again."
    echo "   If you believe this is a false positive, contact your PillarShield admin."
    exit 1
  fi

  echo ""
  echo "🛡️  All content passed governance. Pushing."

done

exit 0

What the developer sees

On a clean push:

$ git push origin main
🛡️  PillarShield: checking 3 content file(s)...

✅ Passed:  docs/getting-started.md
✅ Passed:  docs/api-reference.md
✅ Passed:  blog/product-update.md

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
  Checked: 3  |  Passed: 3  |  Blocked: 0
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

🛡️  All content passed governance. Pushing.

On a violation:

$ git push origin main
🛡️  PillarShield: checking 3 content file(s)...

✅ Passed:  docs/getting-started.md
❌ BLOCKED: docs/api-reference.md
   Status: VIOLATION_DETECTED | Request ID: 4f1f0b77-8c5c-4b2a-9b25-0b3a2f5f2f33
   → [PII_PHONE] PII detected: phone number
   → [PII_EMAIL] PII detected: email address

✅ Passed:  blog/product-update.md

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
  Checked: 3  |  Passed: 2  |  Blocked: 1
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

🚫 Push blocked. Fix the violations above and try again.
   If you believe this is a false positive, contact your PillarShield admin.

Notes

  • Fail-open by default: If PILLARSHIELD_API_KEY is not set, the hook prints a warning and allows the push. This prevents the hook from blocking developers who haven't configured it yet.
  • Override: Developers can bypass with git push --no-verify. This is intentional. The hook is a safety net, not a prison. The audit log records the override decision if your CMS connector is also in the loop.
  • Content patterns: Edit the CONTENT_PATTERNS variable at the top to match your repo. If your content is in a specific directory, use content/*.md instead.
  • Team enforcement: For mandatory team-wide hooks, use Lefthook or Husky with the hook committed to the repo. Or enforce server-side with a CI check (see the GitHub Actions example above).
  • Request IDs: Every response includes a request_id. Include it if you contact support.

Response Handling

The API always returns HTTP 200 for governance decisions. Use the status and is_compliant fields to determine outcome.

Status Values

  • COMPLIANT — Content passed all checks. is_compliant: true.
  • VIOLATION_DETECTED — One or more violations. is_compliant: false. Check violations array.
  • AUTH_ERROR — Invalid or inactive API key. is_compliant: false.
  • PLAN_LIMIT — Monthly quota exhausted. is_compliant: false.
  • INTERNAL_ERROR — Server-side failure. is_compliant: false. Content was not evaluated.

Violation Types

Each violation includes type, severity (error, warning, info), and message.

  • Deterministic: PII_EMAIL, PII_PHONE, PII_SSN, PII_CARD, CREDENTIAL_LEAKAGE, GENERIC (prohibited terms)
  • AI-sourced: PII_INDICATOR_SSN, PII_INDICATOR_CREDENTIAL, PII_INDICATOR_OTHER, TONE_ADVISORY, TONE_SCORE
  • Moderation: MODERATION (hate, harassment, violence, sexual content)

Only error severity violations block content. warning and info are advisory.

Usage Quotas

Check your current usage programmatically.

Endpoint: POST /usage

curl -s -X POST https://api.pillarshield.co/pillarshield-governance/usage \
  -H "Content-Type: application/json" \
  -d '{"api_key": "psk_YOUR_KEY"}'

Returns:

{
  "status": "OK",
  "client_id": "CVE-1A2B3C4D",
  "plan": "protect",
  "governance_hub": "default",
  "limits": {
    "max_checks_month": 2000,
    "max_llm_checks_month": 2000
  },
  "usage": {
    "month_key": "2026-02",
    "checks_total": 1245,
    "checks_llm": 34,
    "remaining_checks_month": 755,
    "remaining_llm_checks_month": 1966
  }
}

You can also query a specific month: {"api_key": "psk_...", "month_key": "2026-01"}

Best Practices

  1. Decide your failure mode. If the API is unreachable (network error, 5xx), should your app block the save (fail closed) or allow it with a warning (fail open)? PillarShield's AI checks are fail-open by design — if the AI service fails, only deterministic rules block. Your integration should make the same decision.
  2. Strip HTML before sending. The API evaluates text. While it handles some markup, stripping tags yields better results for prohibited term and PII detection.
  3. Send context. Include page_type and page_title when possible. It helps AI checks understand whether "confidential" in a Legal Policy page is expected vs. a blog post where it might indicate leaked internal content.
  4. Use request_id for debugging. Every response includes a unique request_id. Include it if you contact support or need to cross-reference with the audit log.
  5. Monitor usage. Call POST /usage periodically or build it into your admin dashboard. If you're approaching your plan limit, add a usage block before you hit the cap.