﻿---
name: fraud-detection
description: >
  Instructs AI agents how to integrate an external fraud engine with Kyriba
  using the outbound Fraud Detection API. Poll for transfers pending screening
  and submit PASS/HIT results back asynchronously.
version: 1.0.0
scopes:
  - fraud-detection-scope
authors:
  - kyriba
tags:
  - kyriba
  - payments
  - fraud
  - api
---

> **Auth** — POST {AUTH_BASE_URL}/oauth/token · Basic base64(CLIENT_ID:CLIENT_SECRET) · no scope.
> Use token_type verbatim — do not hardcode `Bearer`. On 401: retry once with the other scheme (`token` vs `Bearer`). On 429: wait Kyriba-Customer-Rate-Limit-Reset.

# Kyriba API Skill: Fraud Detection

>  **Not the same as Fraud Detection SPI.** The SPI is an inbound webhook (Kyriba calls your server synchronously). This API is **outbound polling** - your system calls Kyriba to get pending transfers and submits results asynchronously.

---

## Required Kyriba Permission

```
fraud-detection-scope
```

This is a Kyriba access permission — not an OAuth scope parameter. Do NOT add `scope=` to the token request.

---

## Base URL

```
https://api.demo.kyriba.com/api/fraud/v1
```

---

## Endpoints

| Method | Path | Description |
|--------|------|-------------|
| `GET` | `/api/fraud/v1/fraud-detection/transfers/to-be-screened` | Transfers pending screening (full transfer fields) |
| `GET` | `/api/fraud/v1/fraud-detection/transfers/screened-with-alerts` | Transfers with OPEN alerts (slim response) |
| `GET` | `/api/fraud/v1/fraud-detection/transfers/screened-with-alerts/searchable-fields` | Filterable fields for screened-with-alerts |
| `POST` | `/api/fraud/v1/fraud-detection/transfers/validation-result` | Submit screening result(s) |

---

## Pagination

Uses flat `offset` / `limit`. Response envelope key: **`pageMetadata`** (not `metadata`).

```json
{
  "pageMetadata": { "total": 8, "count": 1, "limit": 1, "offset": 0,
    "links": { "current": "...", "next": "..." }
  },
  "kyribaCustomer": { "uuid": "...", "code": "ACMECORP" },
  "results": [ ... ]
}
```

---

## Workflow

```
1. GET /fraud-detection/transfers/to-be-screened   -> transfers with full fields + version
2. Run your fraud engine
3. POST /fraud-detection/transfers/validation-result -> PASS or HIT
```

---

## Response Fields - to-be-screened

Full transfer data. Use `uuid` and `version` when submitting results to POST.

| Field | Description |
|---|---|
| `uuid` | Transfer UUID - **required in POST** |
| `version` | Concurrency version - **required in POST** |
| `transactionNumber` | Kyriba transaction number |
| `screeningContext` | `URGENT` or `NORMAL` |
| `transactionType` | e.g. `DOMESTIC_TRANSFER` |
| `amount` | Amount |
| `currency.code` | ISO currency code |
| `transactionDate` | ISO 8601 |
| `counterparty` | Beneficiary details |

## Response Fields - screened-with-alerts

>  Slim response - only `uuid` and `alerts[]`. Does **not** include `version` or `screeningContext`.

| Field | Description |
|---|---|
| `uuid` | Transfer UUID |
| `alerts[]` | Array of open alerts |
| `alerts[].uuid` | Alert UUID |
| `alerts[].description` | Alert description (e.g. "Duplicate transaction") |
| `alerts[].status` | `OPEN`, `FALSE_POSITIVE`, or `CONFIRMED_SUSPICION` |

### Filterable fields for screened-with-alerts
```
uuid, transactionNumber
```

---

## POST - Submit Result

**PASS (no fraud):**
```http
POST /api/fraud/v1/fraud-detection/transfers/validation-result
Content-Type: application/json

{
  "results": [{ "uuid": "<transfer-uuid>", "version": 1, "alerts": [] }]
}
```

**HIT (fraud detected):**
```json
{
  "results": [{
    "uuid": "<transfer-uuid>",
    "version": 1,
    "alerts": [{ "uuid": "<alert-uuid>", "description": "Reason", "status": "OPEN" }]
  }]
}
```

**Error in your engine:**
```json
{ "results": [{ "uuid": "<transfer-uuid>", "version": 1, "isError": true }] }
```

>  `alerts` and `isError` are mutually exclusive.

---

## Critical Rules

1. Always capture `version` from `to-be-screened` - required for POST
2. `alerts: []` = PASS. Omitting `alerts` is not the same
3. `screened-with-alerts` returns only `uuid` + `alerts[]` - **not** `version` or `screeningContext`
4. Use `isError: true` for engine failures - not `alerts: []`
5. Submit multiple results in one POST `results[]` array

---

## Error Reference

| Status | Meaning |
|---|---|
| `200` | Results processed |
| `400` | Invalid body |
| `403` | Required Kyriba permission not configured on your API client |
| `409` | Version conflict |

---

## OpenAPI Spec

- OpenAPI: `https://developer.kyriba.com/static/apis/fraud-detection/fraud-detection.yaml`