> ## Documentation Index
> Fetch the complete documentation index at: https://docs.superform.xyz/llms.txt
> Use this file to discover all available pages before exploring further.

# Building Institutional Automation

> Programmatic auth, safety patterns, and an end-to-end example for automated vault operations.

Automated institutional operators monitor vault state 24/7, define rule-based strategies via the Strategy Canvas, pause services on anomaly detection, and execute operations without human latency. SuperVaults supports full programmatic control via the Erebor management API and the Strategy Engine.

## Authentication for Automation

Generate a JWT without the browser:

1. Sign an EIP-712 auth message with your wallet's private key
2. Submit the signature to Dynamic.xyz to receive a JWT
3. Pass the JWT as `Authorization: Bearer <jwt>` on all requests

Verify against Erebor:

<CodeGroup>
  ```python Python theme={null}
  import requests

  EREBOR_URL = "https://erebor.superform.xyz"
  JWT = "<your-jwt>"

  headers = {"Authorization": f"Bearer {JWT}"}

  resp = requests.get(f"{EREBOR_URL}/api/v1/auth/me", headers=headers)
  resp.raise_for_status()
  user = resp.json()

  print(f"Authenticated as: {user['wallet_address']}")
  print(f"Vaults: {[v['vault_address'] for v in user['vaults']]}")
  ```

  ```typescript TypeScript theme={null}
  const EREBOR_URL = "https://erebor.superform.xyz";
  const JWT = process.env.CURATOR_JWT!;

  const headers = { Authorization: `Bearer ${JWT}` };

  const resp = await fetch(`${EREBOR_URL}/api/v1/auth/me`, { headers });
  if (!resp.ok) throw new Error(`Auth failed: ${resp.status}`);

  const user = await resp.json();
  console.log(`Authenticated as: ${user.wallet_address}`);
  console.log(`Vaults: ${user.vaults.map((v: any) => v.vault_address)}`);
  ```
</CodeGroup>

## Safety Principles

<Warning>
  Automated scripts have full manager-level access. A bug can modify live vault configuration. Apply these safeguards before deploying.
</Warning>

### Test on Base First

Base (chain ID: 8453) has lower gas costs and faster confirmation times. Validate all automation against Base vaults before Ethereum mainnet.

### Rate Limit Polling

| Data               | Recommended Interval |
| ------------------ | -------------------- |
| Vault state        | 15s                  |
| Analytics overview | 60s                  |
| Service health     | 30s                  |

### Circuit Breakers

Stop automation on consecutive API failures:

```python theme={null}
MAX_CONSECUTIVE_ERRORS = 5
consecutive_errors = 0

def safe_api_call(fn):
    global consecutive_errors
    try:
        result = fn()
        consecutive_errors = 0
        return result
    except Exception as e:
        consecutive_errors += 1
        if consecutive_errors >= MAX_CONSECUTIVE_ERRORS:
            alert_and_stop(f"Circuit breaker: {consecutive_errors} consecutive errors")
        raise
```

### Validate Before Writing

Before pushing strategy or configuration changes:

1. Fetch current state
2. Validate the proposed change is meaningful
3. Confirm yield sources are all still whitelisted
4. Apply change
5. Re-fetch and verify the change was applied

## Example: Strategy-Driven Vault Monitoring

Fetches vault state from Erebor and checks key health indicators:

<CodeGroup>
  ```python Python theme={null}
  import requests
  import time

  EREBOR_URL = "https://erebor.superform.xyz"
  JWT = "<your-jwt>"
  CHAIN_ID = 8453
  VAULT_ADDRESS = "0xYourVault..."

  headers = {"Authorization": f"Bearer {JWT}"}

  def get_vault_state():
      resp = requests.get(
          f"{EREBOR_URL}/api/v1/vaults/{CHAIN_ID}/{VAULT_ADDRESS}",
          headers=headers
      )
      resp.raise_for_status()
      return resp.json()

  def check_vault_health():
      vault = get_vault_state()

      # Check PPS staleness
      remaining = vault.get("remaining_staleness_time", 9999)
      if remaining < 3600:
          print(f"WARNING: PPS expires in {remaining // 60} minutes")

      # Check pause status
      if vault.get("is_paused"):
          print("CRITICAL: Vault is paused onchain")

      # Check TVL
      tvl = vault.get("tvl_usd", 0)
      print(f"TVL: ${tvl:,.0f} | PPS staleness: {remaining}s")

  if __name__ == "__main__":
      while True:
          try:
              check_vault_health()
          except Exception as e:
              print(f"Error: {e}")
          time.sleep(60)
  ```

  ```typescript TypeScript theme={null}
  const EREBOR_URL = "https://erebor.superform.xyz";
  const JWT = process.env.CURATOR_JWT!;
  const CHAIN_ID = 8453;
  const VAULT_ADDRESS = "0xYourVault...";
  const headers = { Authorization: `Bearer ${JWT}` };

  async function getVaultState() {
    const resp = await fetch(
      `${EREBOR_URL}/api/v1/vaults/${CHAIN_ID}/${VAULT_ADDRESS}`,
      { headers }
    );
    if (!resp.ok) throw new Error(`Vault fetch failed: ${resp.status}`);
    return resp.json();
  }

  async function checkVaultHealth() {
    const vault = await getVaultState();

    const remaining = vault.remaining_staleness_time ?? 9999;
    if (remaining < 3600) {
      console.warn(`WARNING: PPS expires in ${Math.floor(remaining / 60)} minutes`);
    }

    if (vault.is_paused) {
      console.error("CRITICAL: Vault is paused onchain");
    }

    console.log(`TVL: $${vault.tvl_usd?.toLocaleString()} | Staleness: ${remaining}s`);
  }

  setInterval(checkVaultHealth, 60_000);
  ```
</CodeGroup>

## Next Steps

<CardGroup cols={2}>
  <Card title="Monitoring" icon="chart-line" href="/curate/automation/monitoring">
    Poll vault state and build alert conditions.
  </Card>

  <Card title="Webhooks" icon="webhook" href="/curate/automation/webhooks">
    Audit logs as interim event stream.
  </Card>
</CardGroup>
