﻿---
name: data
description: >
  Instructs AI agents how to upload files to Kyriba using the Data API.
  Returns a fileId used by Process Templates to trigger import workflows.
  Always used as step 1 of the data import pattern.
version: 1.0.0
scopes:
  - data-scope
authors:
  - kyriba
tags:
  - kyriba
  - connectivity
  - data
  - 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: Data (File Upload)

---

## Required Kyriba Permission

```
data-scope
```

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

---

## Purpose

Upload one or more files to Kyriba. Returns a `fileId` (UUID) per file. Pass these `fileId` values as `fileIds` query parameters when running an import Process Template.

---

## Base Path

```
/v1/data
```

---

## Endpoints

| Method | Path | Description |
|--------|------|-------------|
| `POST` | `/v1/data/files` | Upload 1–10 files |

---

## Upload Request

````
POST /v1/data/files
Content-Type: multipart/form-data
Authorization: {token_type} {access_token}
````

Form fields:

| Field | Required | Description |
|---|---|---|
| `file0` |  Yes | First file (binary) |
| `file1` – `file9` | No | Additional files (up to 9 more) |

> Upload limit: **10 MB per request** (meta-skill limit). Up to **10 files** per request.

---

## Upload Response (`201 Created`)

```json
[
  {
    "fileId": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
    "fileName": "payments_2025-07-08.csv",
    "hashSum": "3EA3339909DF0F0E5528D3D9BDDF1756"
  }
]
```

One entry per file. The `fileId` is the value to pass as `fileIds` to the Process Templates run endpoint.

---

## Full Import Pattern

```
Step 1 - Upload file:
POST /v1/data/files
  -> fileId = "xxxxxxxx-..."

Step 2 - Run import template:
POST /v1/process-templates/{templateRef}/run?fileIds={fileId}
  -> taskId = "yyyyyyyy-..."

Step 3 - Poll status:
GET /v1/process-templates/{taskId}/status
  -> poll every 5s until Complete | Warning | Failed
```

>  Always verify `taskId` is present in the run response for every `fileId`. Checking only the HTTP status code is misleading when the file has already been integrated.

---

## Python Upload Example

````python
import requests

def upload_file(base_url, token_type, access_token, file_path):
    url = f"{base_url}/v1/data/files"
    headers = {"Authorization": f"{token_type} {access_token}"}
    with open(file_path, "rb") as f:
        resp = requests.post(url, headers=headers, files={"file0": f})
    resp.raise_for_status()
    return resp.json()[0]["fileId"]  # pass this as ?fileIds= to Process Templates
````

---

## Critical Rules

1. `file0` is the **only required** field - `file1`–`file9` are optional
2. Use `multipart/form-data` - JSON body (`application/json`) is not accepted (`415`)
3. The returned `uuid` is consumed once - re-uploading creates a new `uuid`
4. Upload limit: **10 MB per request**

---

## Error Reference

| Status | Meaning |
|---|---|
| `201` | Upload successful - returns array of file objects |
| `400` | Bad request (e.g. empty file) |
| `403` | Required Kyriba permission not configured on your API client |
| `415` | Wrong Content-Type (must be `multipart/form-data`) |

---

## OpenAPI Spec & Postman Collection

- OpenAPI: `https://developer.kyriba.com/static/apis/data/data.yaml`
- Postman: `https://developer.kyriba.com/static/apis/data/data-postman-collection.json`