HomeGuidesAPI Reference
Log In
Guides

Implementation Guide

Create a risk report flow (one-time or ongoing), exchange the authorization code, and fetch the report.

Getting Started

Three API calls to generate a risk report. Risk Insights supports both one-time and ongoing access - choose the flow that matches your use case.

sequenceDiagram
    participant Y as Your server
    participant N as nrich API
    participant U as User (Widget)

    Y->>N: POST /onetime/risk-report (or /ongoing/risk-report)
    N-->>Y: { location: "https://widget...", id: "abc123" }
    Y->>U: Redirect user to location URL
    U->>U: Bank selection + SCA
    U->>Y: Redirect to redirect_uri?code=...&flow_id=abc123
    Y->>N: POST /auth/token (grant_type: authorization_code)
    N-->>Y: { access_token, refresh_token }
    Y->>N: GET /rest/risk-reports/abc123
    N-->>Y: Risk report JSON
Step 1

Create a Widget link for a risk report

Step 2

Exchange the authorization code for an access token

Step 3

Fetch the risk report


1. Create the Widget link

Use for a single risk snapshot (e.g. at loan origination).

POST https://api.finx-s.qwist.cloud/onetime/risk-report

{
  "redirect_uri": "https://my-app.example.com/callback",
  "language": "de",
  "reporting_period": 12,
  "name": "Maria Müller",
  "accounts": [
    { "id": "DE89370400440532013000" }
  ],
  "account_types": ["Giro account"],
  "allow_multi_selection": false
}

Parameters:

ParameterRequired for ongoingDescription
redirect_uriWhere the user is returned after the flow.
user_id✅ (ongoing only)Your unique identifier for this user. Allows multiple bank connections per user.
nameThe user's claimed name. If provided, the report includes a name-match verification result. If omitted, no name verification is performed.
reporting_periodMonths of transaction history to analyse. API minimum: 4. Recommended for underwriting: 12. Default: 12. Cannot be set at the same time as sync_period.
accountsPre-fill the user's IBAN. Skips bank selection.
account_typesRestrict to specific account types. Skips the type selection screen.
allow_multi_selectionfalse restricts to one account - recommended for clean risk analysis.
save_secretsOngoing only. false (default): user chooses. true: always save credentials (enables autosync).
languageWidget UI language: de, en, es, fr, ar. Default: de.
📘

Include the name

Passing name adds account holder verification to the report at no extra step for the user. Without it, the verification_result field is absent from the report entirely.

📘

Single account for cleaner analysis

Set allow_multi_selection: false and pass the user's primary Giro account IBAN. This ensures income and expense aggregations are tied to one account, making the report easier to interpret.

Response:

{
  "location": "https://widget.qwist.cloud/?token=eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...",
  "id": "b7c4e2a9-3f8d-4a1b-9e5c-2d6f8a0b4e7c"
}
🚧

Store the id

This is the report ID you'll need in step 3. It is also returned as flow_id in the callback. If you capture it from the callback, you don't need to store it separately - but it's good practice to store it for support queries.


2. Exchange the authorization code


2. Exchange the authorization code

After the user completes the Widget flow, they are redirected to your redirect_uri with:

ParameterDescription
codeAuthorization code valid for 1 hour. Present only on success.
flow_idUnique identifier for this flow. Matches the id from step 1.
successtrue if the user completed the flow.
aborttrue if the user cancelled.
stateYour original state value, echoed back for session correlation.

Example callback URL:

https://my-app.example.com/callback
  ?code=eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...
  &flow_id=3ca31c37-986a-454e-ad64-8e97143c86bc
  &success=true
  &abort=false
  &state=user_42_session_9f3a
🚧

If abort is true

The user cancelled - no code is returned. Show a dismissal state and let the user retry. Do not attempt to exchange a missing code.

Exchange the code for tokens

POST https://api.finx-s.qwist.cloud/auth/token

// Request
{
  "grant_type": "authorization_code",
  "code": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...",
  "redirect_uri": "https://my-app.example.com/callback"
}
// Response
{
  "access_token": "AoFmNJLDTW8jQtGSJ1iZeeoLiwNZ2ihz3iiCHGpuvE439nppuY...",
  "token_type": "Bearer",
  "expires_in": 3600,
  "refresh_token": "RTfI2WNyK78NozupDH9ai8GPRbjjdVsXPPt...",
  "scope": "accounts=ro balance=ro transactions=ro offline"
}

Use the access_token as a Bearer token in the Authorization header for all subsequent requests. Store both tokens - you will need the refresh_token to keep access alive without re-asking the user.

TokenLifetimeNotes
Authorization code1 hourSingle use
Access token1 hourAuthorization: Bearer {token}
Refresh token90 daysEach use returns a new token - replace it immediately
📘

Token storage

Token lengths vary. Allocate at least 2048 bytes per token field in your database - do not use fixed-size columns.

Refreshing the access token

Access tokens expire after 1 hour. For ongoing access, refresh server-side before expiry rather than waiting for a 401.

POST https://api.finx-s.qwist.cloud/auth/token

// Request
{
  "grant_type": "refresh_token",
  "refresh_token": "RTfI2WNyK78NozupDH9ai8GPRbjjdVsXPPt..."
}
// Response
{
  "access_token": "BpGnOKMEUX9kRuHTK2jZfpoMjxOa3jhQtjjDIHqwvF540oqqvZ...",
  "token_type": "Bearer",
  "expires_in": 3600,
  "refresh_token": "SUgJ3XOzL89PazvrEI0bj9HQSckkwWtYQQu...",
  "scope": "accounts=ro balance=ro transactions=ro offline"
}
🚧

Replace the refresh token immediately

Each call invalidates the previous refresh_token and returns a new one. Persist the new token before using the new access_token - using the old refresh token will fail with 401.



3. Fetch the risk report

You need the id from step 1 (or flow_id from the callback) and the access_token. See Fetching your Risk Report.