# Quickstart

Get started with Verso API by running a complete payroll calculation. This guide walks you through the entire workflow from user creation to retrieving detailed pay results.

:::info[Route Pattern]
All API endpoints use the **Partner route** pattern: `/partners/{partnerId}/tenants/{tenantId}/{resource}`. See [Authentication](/authentication) for details.
:::

:::tip[Multi-Tenant Architecture]
You can manage multiple tenants (clients) using the same API key. Simply change the `tenantId` in the URL to switch between clients. Each tenant's data is completely isolated.
:::

## Prerequisites

Before you begin, ensure you have:

- **Partner Account**: Your Partner ID provided by Verso (e.g., `5`)
- **API Key**: Bearer token linked to your Partner ID
- **HTTP Client**: cURL, Postman, or any REST client

:::tip[Environment Discovery]
After creating your tenant (Step 1), Step 2 shows how to discover your actual division, payroll, and payrun IDs.
:::

**Example identifiers used in this guide:**
- Partner ID: `5`
- Tenant ID: `10`
- Division ID: `3` (discovered in Step 2)
- Payroll ID: `3` (discovered in Step 2)

:::info[First Time Setup]
Step 1 shows how to register a tenant to your partner account. This is only needed once per client - after that, you can skip to Step 3 for subsequent payruns.
:::

:::tip
Download our <a href="/Verso_Payroll_Forecast_Workflow.postman_collection.json" download="Verso_Payroll_Forecast_Workflow.postman_collection.json">Postman Collection</a> to test the complete workflow with pre-configured requests.
:::

## Complete Workflow

### Step 1: Create a Tenant

Create a minimal PayrollEngine tenant for the client. `externalId` is your stable dossier ID and
makes the request idempotent; Verso generates the internal PayrollEngine identifier.

```bash title="Register a Tenant"
curl -X POST "https://api.versohq.io/partners/5/tenants" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "externalId": "my-client-abc",
    "name": "My client",
    "culture": "fr-FR"
  }'
```

**Response:**
```json
{
  "id": 1,
  "partnerId": 5,
  "tenantId": 42,
  "tenantIdentifier": "partner-5-550e8400e29b41d4a716446655440000",
  "externalId": "my-client-abc",
  "name": "My client",
  "culture": "fr-FR",
  "status": "active",
  "created": "2026-01-07T10:00:00Z"
}
```

:::info
This endpoint creates a bare tenant only: no employee, payroll, regulation or DSN configuration is
provisioned. For a complete French SASU mandataire dossier, use
`POST /partners/{partnerId}/tenants/mandataires`. Reusing the same `externalId` returns the same tenant.
:::

### Step 2: Discover Your Environment

After registering your tenant, discover the IDs you'll need for subsequent steps:

```bash title="Get Divisions"
curl -X GET "https://api.versohq.io/partners/5/tenants/10/divisions" \
  -H "Authorization: Bearer YOUR_API_KEY"
```

Save the `id` and `name` of your target division.

```bash title="Get Payrolls"
curl -X GET "https://api.versohq.io/partners/5/tenants/10/payrolls" \
  -H "Authorization: Bearer YOUR_API_KEY"
```

Save the `id` of your payroll.

```bash title="Get Payruns"
curl -X GET "https://api.versohq.io/partners/5/tenants/10/payruns" \
  -H "Authorization: Bearer YOUR_API_KEY"
```

Save the `id` of your payrun (e.g., `3`). You'll need it when starting payrun jobs in Step 6.

### Step 3: Create a User

Create a user who will manage payroll operations:

```bash title="Create User"
curl -X POST "https://api.versohq.io/partners/5/tenants/10/users" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "identifier": "support@company.com",
    "firstName": "Support",
    "lastName": "Admin",
    "culture": "fr-FR",
    "userType": "User"
  }'
```

**Response:**
```json
{
  "id": 7,
  "identifier": "support@company.com",
  "firstName": "Support",
  "lastName": "Admin"
}
```

Save the `id` value (e.g., `7`) for subsequent requests.

### Step 4: Create an Employee

Create an employee and assign them to a division:

```bash title="Create Employee"
curl -X POST "https://api.versohq.io/partners/5/tenants/10/employees" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "identifier": "jean.dupont@company.com",
    "firstName": "Jean",
    "lastName": "Dupont",
    "culture": "fr-FR",
    "divisions": ["{divisionName}"]
  }'
```

**Response:**
```json
{
  "id": 2,
  "identifier": "jean.dupont@company.com",
  "firstName": "Jean",
  "lastName": "Dupont"
}
```

Replace `{divisionName}` with the division name discovered in Step 2.

Save the `id` value (e.g., `2`) for subsequent requests.

### Step 5: Add Employee Case Values

Add all payroll data for the employee using the native `CaseChangeSetup` format. The payload uses a nested structure: a root `case` with its `values`, and `relatedCases` for additional domains (remuneration, expenses, etc.).

```bash title="Add Case Values"
curl -X POST "https://api.versohq.io/partners/5/tenants/10/payrolls/3/cases" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "userId": 7,
    "employeeId": 2,
    "divisionId": 3,
    "forecast": "quickstart-test",
    "case": {
      "caseName": "Identite",
      "values": [
        { "caseFieldName": "Nom", "value": "Dupont", "start": "2025-01-01T00:00:00Z" },
        { "caseFieldName": "Prenom", "value": "Jean", "start": "2025-01-01T00:00:00Z" }
      ],
      "relatedCases": [
        {
          "caseName": "Remuneration",
          "values": [
            { "caseFieldName": "HeuresTravaillees", "value": "151.67", "start": "2025-01-01T00:00:00Z" },
            { "caseFieldName": "TauxHoraire", "value": "12.50", "start": "2025-01-01T00:00:00Z" }
          ]
        },
        {
          "caseName": "Frais",
          "values": [
            { "caseFieldName": "NombreRepas", "value": "15", "start": "2025-01-01T00:00:00Z" },
            { "caseFieldName": "MontantRepas", "value": "9.50", "start": "2025-01-01T00:00:00Z" },
            { "caseFieldName": "IndemniteKilometrique", "value": "250", "start": "2025-01-01T00:00:00Z" },
            { "caseFieldName": "Regularisation", "value": "50.00", "start": "2025-01-01T00:00:00Z" }
          ]
        },
        {
          "caseName": "PAS",
          "values": [
            { "caseFieldName": "TauxPAS", "value": "0.05", "start": "2025-01-01T00:00:00Z" }
          ]
        },
        {
          "caseName": "PrevoyanceMutuelle",
          "values": [
            { "caseFieldName": "MutuelleObligatoire", "value": "24.75", "start": "2025-01-01T00:00:00Z" },
            { "caseFieldName": "MutuelleFacultative", "value": "49.50", "start": "2025-01-01T00:00:00Z" },
            { "caseFieldName": "TauxPrevoyance", "value": "0.0089", "start": "2025-01-01T00:00:00Z" }
          ]
        }
      ]
    }
  }'
```

**Response (201 Created):**
```json
{
  "id": 15,
  "userId": 7,
  "employeeId": 2,
  "divisionId": 3
}
```

:::tip[Forecast Mode]
The `forecast` field is set at the root level of the payload (not inside `case`). It runs a simulation without affecting official payroll records. Remove this field when running real payroll. See [Real vs Forecast Mode](/guides/real-vs-forecast).
:::

### Step 6: Start the Payrun Job

:::info[Payrun and user lookup]
The launch request uses the exact `payrunName` and `userIdentifier` returned by `GET /payruns`
and `GET /users`. Match those resources by the IDs created during setup; do not select the first
item in a multi-tenant collection.
:::

Launch the payroll calculation:

```bash title="Start Payrun"
curl -X POST "https://api.versohq.io/partners/5/tenants/10/payruns/jobs" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Payrun_Jan2025",
    "payrunName": "Monthly",
    "userIdentifier": "payroll-admin@company.com",
    "forecast": "quickstart-test",
    "periodStart": "2025-01-01T00:00:00Z",
    "reason": "January 2025 payroll",
    "employeeIdentifiers": ["jean.dupont@company.com"]
  }'
```

**Response (202 Accepted):**
```json
{
  "id": 8,
  "name": "Payrun_Jan2025",
  "jobStatus": "Process",
  "jobResult": "Full",
  "periodStart": "2025-01-01T00:00:00Z",
  "periodEnd": "2025-01-31T23:59:59.9999999Z",
  "totalEmployeeCount": 1,
  "processedEmployeeCount": 0,
  "message": "Payrun job queued for background processing"
}
```

Save the `id` value (e.g., `8`). The job runs asynchronously — poll its status until it completes:

```bash title="Poll Job Status"
curl -X GET "https://api.versohq.io/partners/5/tenants/10/payruns/jobs/8" \
  -H "Authorization: Bearer YOUR_API_KEY"
```

Wait until `jobEnd` is set (or `jobStatus` is `Forecast`/`Complete`) before retrieving results.
Upstream v1.0.1 can return a successfully calculated asynchronous job to `Draft`, so `Draft`
without `jobEnd` is still queued or running. Treat `Abort` and `Cancel` as failures.

Job completion and result persistence are two separate asynchronous boundaries. After the job is
terminal, retry the exact `payrunJobId` query with bounded backoff until it returns a result set.
Do this after every recalculation before reading a summary or running DSN control; an empty `200`
response is transient and must never be replaced with a result from another job.

### Step 7: Get Detailed Results

Retrieve the complete wage type breakdown:

```bash title="Get Results"
curl -X GET "https://api.versohq.io/partners/5/tenants/10/payrollresults/sets?payrunJobId=8" \
  -H "Authorization: Bearer YOUR_API_KEY"
```

:::tip[Pagination for Large Batches]
For payrun jobs with many employees (100+), use pagination to avoid timeouts:
```bash
# Fetch 50 results at a time
curl -X GET "https://api.versohq.io/partners/5/tenants/10/payrollresults/sets?payrunJobId=8&top=50&skip=0"
curl -X GET "https://api.versohq.io/partners/5/tenants/10/payrollresults/sets?payrunJobId=8&top=50&skip=50"
# ... continue until all results are fetched
```
:::

**Response (array of result sets):**
```json
[
  {
    "wageTypeResults": [
      { "wageTypeNumber": 100, "wageTypeName": "SalaireBase", "value": 1895.88 },
      { "wageTypeNumber": 600, "wageTypeName": "IndemniteRepas", "value": 142.5 },
      { "wageTypeNumber": 611, "wageTypeName": "IndemniteKilometrique", "value": 250.0 },
      { "wageTypeNumber": 650, "wageTypeName": "Regularisation", "value": 50.0 },
      { "wageTypeNumber": 900, "wageTypeName": "TotalBrut", "value": 2338.38 },
      { "wageTypeNumber": 910, "wageTypeName": "TotalCotisationsSalariales", "value": 395.11 },
      { "wageTypeNumber": 920, "wageTypeName": "TotalCotisationsPatronales", "value": 701.78 },
      { "wageTypeNumber": 930, "wageTypeName": "TotalAllegements", "value": -531.00 },
      { "wageTypeNumber": 950, "wageTypeName": "NetImposable", "value": 1627.43 },
      { "wageTypeNumber": 960, "wageTypeName": "PAS", "value": 81.37 },
      { "wageTypeNumber": 980, "wageTypeName": "TotalIndemnites", "value": 442.5 },
      { "wageTypeNumber": 990, "wageTypeName": "NetAPayer", "value": 1861.90 }
    ],
    "collectorResults": [ ... ],
    "payrunResults": [ ... ]
  }
]
```

**Key Results:**

- **SalaireBase** (100): Gross salary = 151.67 hours × 12.50 €/hour = 1,895.88 €
- **TotalBrut** (900): Total gross including allowances = 2,338.38 €
- **TotalCotisationsSalariales** (910): Employee social contributions = 395.11 €
- **TotalCotisationsPatronales** (920): Employer social contributions = 701.78 €
- **PAS** (960): Withholding tax (5% of net imposable) = 81.37 €
- **NetAPayer** (990): **Final net salary to pay the employee: 1,861.90 €**

## Workflow Summary

| Step | Method | Endpoint | Purpose |
|------|--------|----------|---------|
| 1 | POST | `/partners/{partnerId}/tenants` | Register a tenant |
| 2 | GET | `/divisions`, `/payrolls`, `/payruns` | Discover environment IDs |
| 3 | POST | `/partners/{partnerId}/tenants/{tenantId}/users` | Create a user |
| 4 | POST | `/partners/{partnerId}/tenants/{tenantId}/employees` | Create an employee with division |
| 5 | POST | `/partners/{partnerId}/tenants/{tenantId}/payrolls/{id}/cases` | Add case values (native CaseChangeSetup format) |
| 6 | POST | `/partners/{partnerId}/tenants/{tenantId}/payruns/jobs` | Start payrun calculation |
| 6b | GET | `/partners/{partnerId}/tenants/{tenantId}/payruns/jobs/{jobId}` | Poll job status until complete |
| 7 | GET | `/partners/{partnerId}/tenants/{tenantId}/payrollresults/sets?payrunJobId={id}` | Get wage type details |

## Next Steps

- [Authentication](/authentication) - Learn about API authentication methods
- [Case Fields Reference](/reference/case-fields) - Complete list of available fields
- [Real vs Forecast Mode](/guides/real-vs-forecast) - Learn when to use forecast for testing vs real mode for production
- [API Reference](/api) - Explore all available endpoints
