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

# Integration Scenarios

> End-to-end test scripts for validating your Zenoo integration against the staging environment.

# Integration Scenarios

End-to-end test scripts for validating your Zenoo integration. Run these against the staging environment before going live.

<Note>Replace `{project_hash}` with your staging project hash and `staging-key` with your staging API key in all examples below.</Note>

## Scenario 1: Company Verification sync end-to-end

Submit a company for verification using sync mode and validate the response.

```bash theme={null}
# Submit company verification (sync)
curl -s -X POST \
  "https://instance.staging.onboardapp.io/api/gateway/execute/{project_hash}/api" \
  -H "Content-Type: application/json" \
  -H "X-API-KEY: staging-key" \
  -H "X-SYNC-TIMEOUT: 30000" \
  -d '{
    "company_name": "Test Company Clean Ltd",
    "registration_number": "TEST001",
    "country": "GB",
    "external_reference": "e2e-kyb-sync-001"
  }' | jq .
```

**Verify the response contains:**

* `overall_verdict` is `"Pass"`, `"Refer"`, or `"Fail"`
* `risk_tier` is `"Low"`, `"Medium"`, or `"High"`
* `company.legal_name` is populated
* `company.verification_status` is `"Verified"`
* `screening` object contains `pep_status`, `sanctions_status`, `adverse_media_status`
* `checks_summary.total` is greater than 0
* `case_reference` starts with `"AML-"`

```bash theme={null}
# Validate response fields
RESPONSE=$(curl -s -X POST \
  "https://instance.staging.onboardapp.io/api/gateway/execute/{project_hash}/api" \
  -H "Content-Type: application/json" \
  -H "X-API-KEY: staging-key" \
  -H "X-SYNC-TIMEOUT: 30000" \
  -d '{"company_name":"Test Company Clean Ltd","registration_number":"TEST001","country":"GB","external_reference":"e2e-kyb-sync-001"}')

echo "$RESPONSE" | jq '{
  verdict: .overall_verdict,
  risk: .risk_tier,
  company_verified: .company.verification_status,
  checks_total: .checks_summary.total,
  pep: .screening.pep_status,
  sanctions: .screening.sanctions_status
}'
```

## Scenario 2: Person Verification async end-to-end

Initiate an async Person Verification journey, extract tokens, and poll for results.

```bash theme={null}
# Step 1: Initiate async journey
INIT_RESPONSE=$(curl -s -X POST \
  "https://instance.staging.onboardapp.io/api/gateway/execute/{project_hash}/init" \
  -H "Content-Type: application/json" \
  -H "X-API-KEY: staging-key" \
  -d '{
    "first_name": "Test",
    "last_name": "Clean",
    "date_of_birth": "1990-01-15",
    "country": "GB",
    "external_reference": "e2e-kyc-async-001"
  }')

echo "Init response:"
echo "$INIT_RESPONSE" | jq .

# Step 2: Extract pull token
PULL_TOKEN=$(echo "$INIT_RESPONSE" | jq -r '.tokens.pull')
echo "Pull token: $PULL_TOKEN"

# Step 3: Poll for results (may return 204 while processing)
sleep 10
RESULT=$(curl -s -w "\n%{http_code}" \
  "https://instance.staging.onboardapp.io/api/gateway/sharable-payload/$PULL_TOKEN")

HTTP_CODE=$(echo "$RESULT" | tail -1)
BODY=$(echo "$RESULT" | head -n -1)

if [ "$HTTP_CODE" = "200" ]; then
  echo "Results ready:"
  echo "$BODY" | jq .
elif [ "$HTTP_CODE" = "204" ]; then
  echo "Still processing. Poll again in 30 seconds."
elif [ "$HTTP_CODE" = "404" ]; then
  echo "Token invalid or expired."
fi
```

**Verify:**

* Init response contains `tokens.pull` and `tokens.start`
* Poll returns `204` (processing) or `200` (results ready)
* Final `200` response contains `overall_verdict` and `risk_tier`

## Scenario 3: Idempotency

Submit the same `external_reference` twice. The second request should return the existing case, not create a duplicate.

```bash theme={null}
REFERENCE="e2e-idempotent-$(date +%s)"

# First request
echo "--- First request ---"
curl -s -X POST \
  "https://instance.staging.onboardapp.io/api/gateway/execute/{project_hash}/api" \
  -H "Content-Type: application/json" \
  -H "X-API-KEY: staging-key" \
  -H "X-SYNC-TIMEOUT: 30000" \
  -d "{
    \"company_name\": \"Test Company Clean Ltd\",
    \"registration_number\": \"TEST001\",
    \"country\": \"GB\",
    \"external_reference\": \"$REFERENCE\"
  }" | jq '{case_reference: .case_reference, verdict: .overall_verdict}'

# Second request (same external_reference)
echo "--- Second request ---"
curl -s -X POST \
  "https://instance.staging.onboardapp.io/api/gateway/execute/{project_hash}/api" \
  -H "Content-Type: application/json" \
  -H "X-API-KEY: staging-key" \
  -H "X-SYNC-TIMEOUT: 30000" \
  -d "{
    \"company_name\": \"Test Company Clean Ltd\",
    \"registration_number\": \"TEST001\",
    \"country\": \"GB\",
    \"external_reference\": \"$REFERENCE\"
  }" | jq '{case_reference: .case_reference, verdict: .overall_verdict}'
```

<Check>
  **Verify:**

  * Both responses return the same `case_reference`
  * No duplicate verification is created
  * The second request completes faster (returns cached result)
</Check>

## Scenario 4: Error handling

Test error responses for common failure modes.

### Missing required field (400)

```bash theme={null}
curl -s -X POST \
  "https://instance.staging.onboardapp.io/api/gateway/execute/{project_hash}/api" \
  -H "Content-Type: application/json" \
  -H "X-API-KEY: staging-key" \
  -H "X-SYNC-TIMEOUT: 15000" \
  -d '{"country": "GB"}' | jq .
```

**Expected:** `400` with `error: "VALIDATION_ERROR"`.

### Invalid API key (401)

```bash theme={null}
curl -s -w "\nHTTP %{http_code}" -X POST \
  "https://instance.staging.onboardapp.io/api/gateway/execute/{project_hash}/api" \
  -H "Content-Type: application/json" \
  -H "X-API-KEY: invalid-key-here" \
  -H "X-SYNC-TIMEOUT: 15000" \
  -d '{"company_name":"Test","registration_number":"TEST001","country":"GB"}'
```

**Expected:** `401` with `error: "UNAUTHORIZED"`.

### Invalid project hash (404)

```bash theme={null}
curl -s -w "\nHTTP %{http_code}" -X POST \
  "https://instance.staging.onboardapp.io/api/gateway/execute/invalid-hash/api" \
  -H "Content-Type: application/json" \
  -H "X-API-KEY: staging-key" \
  -H "X-SYNC-TIMEOUT: 15000" \
  -d '{"company_name":"Test","registration_number":"TEST001","country":"GB"}'
```

**Expected:** `404` with `error: "NOT_FOUND"`.

### Invalid JSON (400)

```bash theme={null}
curl -s -X POST \
  "https://instance.staging.onboardapp.io/api/gateway/execute/{project_hash}/api" \
  -H "Content-Type: application/json" \
  -H "X-API-KEY: staging-key" \
  -d 'this is not json' | jq .
```

**Expected:** `400` with `error: "INVALID_JSON"`.

## Scenario 5: Webhook delivery

Submit a verification and confirm your webhook endpoint receives the event.

### Setup

Before running this test, ensure your staging webhook URL is configured. For local development:

```bash theme={null}
# Terminal 1: Start your webhook listener
node -e "
const http = require('http');
const server = http.createServer((req, res) => {
  let body = '';
  req.on('data', chunk => body += chunk);
  req.on('end', () => {
    console.log('Webhook received:', JSON.stringify(JSON.parse(body), null, 2));
    res.writeHead(200);
    res.end('OK');
  });
});
server.listen(3000, () => console.log('Listening on port 3000'));
"

# Terminal 2: Expose via ngrok
ngrok http 3000
# Note the https URL and configure it as your staging webhook endpoint
```

### Submit verification

```bash theme={null}
# Submit a company verification to trigger a webhook
curl -s -X POST \
  "https://instance.staging.onboardapp.io/api/gateway/execute/{project_hash}/api" \
  -H "Content-Type: application/json" \
  -H "X-API-KEY: staging-key" \
  -H "X-SYNC-TIMEOUT: 30000" \
  -d '{
    "company_name": "Test Company Clean Ltd",
    "registration_number": "TEST001",
    "country": "GB",
    "external_reference": "e2e-webhook-001"
  }'
```

### Verify webhook received

Check your webhook listener for a `verification.completed` event. The payload should include:

* `event_type: "verification.completed"`
* `callback_reference` matching your `external_reference`
* `data` object with the full verification results
* `X-Zenoo-Signature` header for signature verification

### Verify signature

```javascript theme={null}
const crypto = require("crypto");

function verifyWebhookSignature(payload, signature, secret) {
  const expected =
    "sha256=" +
    crypto.createHmac("sha256", secret).update(payload).digest("hex");
  return crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expected));
}
```

## Checklist

Run all five scenarios and confirm:

* [ ] Scenario 1: Sync Company Verification returns a complete compliance report
* [ ] Scenario 2: Async Person Verification init returns tokens, poll returns results
* [ ] Scenario 3: Duplicate `external_reference` returns same case
* [ ] Scenario 4: All four error cases return correct status codes
* [ ] Scenario 5: Webhook received with valid signature

## Next steps

* [Go-Live Checklist](/testing/go-live-checklist) -- Pre-production requirements
* [Test Data](/testing/test-data) -- Additional test payloads
* [Error Handling](/guides/error-handling) -- Production error handling patterns
