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

# Retrieve Onboarding Status

> Retrieve the current onboarding status for a merchant (lightweight endpoint)

## Overview

Retrieves the current onboarding status for a merchant using the onboarding reference number. This is a lightweight endpoint that returns only essential status information without the full onboarding data.

<Info>
  **Ideal for:** Polling the onboarding status, quick status checks in dashboards, or determining if full onboarding data needs to be fetched.
</Info>

<Tip>
  **Lightweight Alternative:** Use this endpoint instead of [Retrieve Onboarding Data](/api-reference/onboarding/retrieve-onboarding-data) when you only need to check the approval status.
</Tip>

## Authentication

This endpoint requires JWT Bearer Token authentication.

```bash theme={null}
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
```

<Warning>
  **Authorization Required:** The authenticated user must be the owner of the onboarding record. Only the user who created the onboarding record can retrieve its status.
</Warning>

## Path Parameters

<ParamField path="refNo" type="string" required>
  The onboarding reference number (UUID v4 format)

  **Format:** Valid UUID v4

  **Example:** `"550e8400-e29b-41d4-a716-446655440000"`

  <Note>
    This is the `referenceNumber` returned when you successfully submit an onboarding request via the [Onboard Merchant](/api-reference/onboarding/onboard-merchant) endpoint.
  </Note>
</ParamField>

## Response

### Success Response

**Status Code:** `200 OK`

<ResponseField name="onboardingStatus" type="string" required>
  Current status of the merchant's onboarding application

  **Values:**

  * `NEW`: Merchant completed onboarding, awaiting initial approval
  * `PENDING`: Merchant updated form after decline, awaiting re-approval
  * `APPROVED`: Merchant has been approved
  * `DECLINED`: Merchant was declined and needs to update their form

  **Example:** `"NEW"`
</ResponseField>

<ResponseField name="onboardingReferenceNumber" type="string" required>
  Unique identifier for the onboarding record (same as path parameter)

  **Format:** UUID v4

  **Example:** `"550e8400-e29b-41d4-a716-446655440000"`
</ResponseField>

<ResponseField name="merchantName" type="string" required>
  Trading name or DBA (Doing Business As) name of the merchant

  **Example:** `"Acme Store"`
</ResponseField>

<ResponseField name="dateCreated" type="string" required>
  Timestamp when the onboarding record was initially created

  **Format:** ISO 8601

  **Example:** `"2024-01-15T10:30:00Z"`
</ResponseField>

<ResponseField name="dateUpdated" type="string">
  Timestamp of the most recent status change

  **Format:** ISO 8601

  **Example:** `"2024-01-20T14:45:00Z"`

  <Note>
    This is the latest date among `ApprovedAt`, `DeclinedAt`, or `PendingAt`. Returns `null` if no status change has occurred (still in NEW status).
  </Note>
</ResponseField>

<ResponseField name="declinedReason" type="string">
  Reason provided by admin when declining the application

  **Example:** `"Missing required documents: SEC Registration Certificate"`

  <Info>
    **Visibility:** Present only when `onboardingStatus` is `DECLINED`. Set to `null` for other statuses.
  </Info>
</ResponseField>

### Response Examples

<CodeGroup>
  ```json New Merchant theme={null}
  {
    "onboardingStatus": "NEW",
    "onboardingReferenceNumber": "550e8400-e29b-41d4-a716-446655440000",
    "merchantName": "Acme Store",
    "dateCreated": "2024-01-15T10:30:00Z",
    "dateUpdated": null,
    "declinedReason": null
  }
  ```

  ```json Approved Merchant theme={null}
  {
    "onboardingStatus": "APPROVED",
    "onboardingReferenceNumber": "550e8400-e29b-41d4-a716-446655440000",
    "merchantName": "Acme Store",
    "dateCreated": "2024-01-15T10:30:00Z",
    "dateUpdated": "2024-01-20T14:45:00Z",
    "declinedReason": null
  }
  ```

  ```json Declined Merchant theme={null}
  {
    "onboardingStatus": "DECLINED",
    "onboardingReferenceNumber": "550e8400-e29b-41d4-a716-446655440000",
    "merchantName": "Acme Store",
    "dateCreated": "2024-01-15T10:30:00Z",
    "dateUpdated": "2024-01-18T09:15:00Z",
    "declinedReason": "Missing required documents: SEC Registration Certificate"
  }
  ```

  ```json Pending Merchant theme={null}
  {
    "onboardingStatus": "PENDING",
    "onboardingReferenceNumber": "550e8400-e29b-41d4-a716-446655440000",
    "merchantName": "Acme Store",
    "dateCreated": "2024-01-15T10:30:00Z",
    "dateUpdated": "2024-01-22T11:00:00Z",
    "declinedReason": null
  }
  ```
</CodeGroup>

### Error Responses

<AccordionGroup>
  <Accordion title="400 Bad Request - Invalid Reference Number Format">
    **Status Code:** `400`

    ```json theme={null}
    {
      "statusCode": 400,
      "message": "Invalid onboarding reference number format",
      "error": "Bad Request",
      "details": {
        "refNumReceived": "invalid-uuid"
      }
    }
    ```

    **Cause:** The reference number provided is not a valid UUID v4 format

    **Solution:** Ensure the reference number is a valid UUID v4 string
  </Accordion>

  <Accordion title="400 Bad Request - Record Does Not Exist">
    **Status Code:** `400`

    ```json theme={null}
    {
      "statusCode": 400,
      "message": "Onboarding record does not exist",
      "error": "Bad Request"
    }
    ```

    **Cause:** No onboarding record found for the authenticated user

    **Possible Reasons:**

    * The authenticated user has no associated business account
    * The business account has no associated business record
    * The onboarding record was deleted

    **Solution:** Verify the user has completed the onboarding process
  </Accordion>

  <Accordion title="400 Bad Request - Reference Number Mismatch">
    **Status Code:** `400`

    ```json theme={null}
    {
      "statusCode": 400,
      "message": "Onboarding reference number mismatch",
      "error": "Bad Request",
      "details": {
        "refNumReceived": "550e8400-e29b-41d4-a716-446655440000"
      }
    }
    ```

    **Cause:** The provided reference number does not match the authenticated user's business record

    **Solution:** Ensure you're using the correct JWT token for the account that owns this onboarding record
  </Accordion>

  <Accordion title="401 Unauthorized">
    **Status Code:** `401`

    ```json theme={null}
    {
      "statusCode": 401,
      "message": "Account not found",
      "error": "Unauthorized"
    }
    ```

    **Cause:** Invalid or missing JWT Bearer token, or account doesn't exist

    **Solution:**

    * Verify the Authorization header contains a valid JWT token
    * Ensure the token hasn't expired
    * Confirm the account exists in the system
  </Accordion>
</AccordionGroup>

## Status Flow Diagram

The onboarding status follows this flow:

```
NEW → APPROVED (direct approval)
NEW → DECLINED → PENDING → APPROVED (decline then resubmit flow)
NEW → DECLINED → PENDING → DECLINED (multiple decline cycles possible)
```

<Steps>
  <Step title="NEW">
    Merchant has completed the onboarding form and submitted it for initial review. Awaiting admin approval.
  </Step>

  <Step title="DECLINED (Optional)">
    Admin reviewed the application and declined it. The `declinedReason` field contains the reason for rejection. Merchant needs to update their information.
  </Step>

  <Step title="PENDING (Optional)">
    Merchant has updated their form after being declined and resubmitted for review. Awaiting admin re-approval.
  </Step>

  <Step title="APPROVED">
    Admin has approved the merchant's onboarding application. Merchant can now process transactions.
  </Step>
</Steps>

<RequestExample>
  ```bash cURL theme={null}
  # Set your Bearer token and reference number
  TOKEN="your_jwt_bearer_token_here"
  REF_NO="550e8400-e29b-41d4-a716-446655440000"

  curl -X GET "https://kyc.sbx.moduluslabs.io/v2/onboard/$REF_NO/status" \
    -H "Authorization: Bearer $TOKEN" \
    -H "Accept: application/json"
  ```

  ```javascript Node.js theme={null}
  const axios = require('axios');

  const BEARER_TOKEN = process.env.BEARER_TOKEN;
  const REFERENCE_NUMBER = '550e8400-e29b-41d4-a716-446655440000';

  async function getOnboardingStatus(refNo) {
    try {
      const response = await axios.get(
        `https://kyc.sbx.moduluslabs.io/v2/onboard/${refNo}/status`,
        {
          headers: {
            'Authorization': `Bearer ${BEARER_TOKEN}`,
            'Accept': 'application/json'
          }
        }
      );

      const { onboardingStatus, merchantName, declinedReason } = response.data;

      console.log(' Status retrieved successfully');
      console.log(`Merchant: ${merchantName}`);
      console.log(`Status: ${onboardingStatus}`);

      if (onboardingStatus === 'APPROVED') {
        console.log(' Onboarding approved!');
      } else if (onboardingStatus === 'DECLINED') {
        console.log(`  Declined: ${declinedReason}`);
      } else if (onboardingStatus === 'PENDING') {
        console.log(' Awaiting re-approval...');
      } else {
        console.log(' Awaiting initial approval...');
      }

      return response.data;

    } catch (error) {
      if (error.response?.status === 401) {
        console.error('  Unauthorized. Check your Bearer token.');
      } else if (error.response?.status === 400) {
        console.error('  Bad Request:', error.response.data.message);
      } else {
        console.error(' Request failed:', error.response?.data || error.message);
      }
      throw error;
    }
  }

  // Usage
  getOnboardingStatus(REFERENCE_NUMBER);
  ```

  ```python Python theme={null}
  import os
  import requests

  BEARER_TOKEN = os.getenv('BEARER_TOKEN')
  REFERENCE_NUMBER = '550e8400-e29b-41d4-a716-446655440000'

  def get_onboarding_status(ref_no):
      """Get onboarding status by reference number"""
      try:
          response = requests.get(
              f'https://kyc.sbx.moduluslabs.io/v2/onboard/{ref_no}/status',
              headers={
                  'Authorization': f'Bearer {BEARER_TOKEN}',
                  'Accept': 'application/json'
              }
          )

          response.raise_for_status()
          data = response.json()

          status = data['onboardingStatus']
          merchant_name = data['merchantName']
          declined_reason = data.get('declinedReason')

          print(' Status retrieved successfully')
          print(f"Merchant: {merchant_name}")
          print(f"Status: {status}")

          if status == 'APPROVED':
              print(' Onboarding approved!')
          elif status == 'DECLINED':
              print(f"  Declined: {declined_reason}")
          elif status == 'PENDING':
              print(' Awaiting re-approval...')
          else:
              print(' Awaiting initial approval...')

          return data

      except requests.exceptions.HTTPError as e:
          if e.response.status_code == 401:
              print('  Unauthorized. Check your Bearer token.')
          elif e.response.status_code == 400:
              print(f"  Bad Request: {e.response.json().get('message')}")
          else:
              print(f' Request failed: {e.response.text}')
          raise

  if __name__ == '__main__':
      get_onboarding_status(REFERENCE_NUMBER)
  ```

  ```php PHP theme={null}
  <?php
  require 'vendor/autoload.php';

  use GuzzleHttp\Client;
  use GuzzleHttp\Exception\RequestException;

  $bearerToken = getenv('BEARER_TOKEN');
  $referenceNumber = '550e8400-e29b-41d4-a716-446655440000';

  function getOnboardingStatus($refNo) {
      global $bearerToken;

      try {
          $client = new Client();
          $response = $client->get(
              "https://kyc.sbx.moduluslabs.io/v2/onboard/$refNo/status",
              [
                  'headers' => [
                      'Authorization' => 'Bearer ' . $bearerToken,
                      'Accept' => 'application/json'
                  ]
              ]
          );

          $data = json_decode($response->getBody(), true);

          $status = $data['onboardingStatus'];
          $merchantName = $data['merchantName'];
          $declinedReason = $data['declinedReason'] ?? null;

          echo " Status retrieved successfully\n";
          echo "Merchant: $merchantName\n";
          echo "Status: $status\n";

          if ($status === 'APPROVED') {
              echo " Onboarding approved!\n";
          } elseif ($status === 'DECLINED') {
              echo "  Declined: $declinedReason\n";
          } elseif ($status === 'PENDING') {
              echo " Awaiting re-approval...\n";
          } else {
              echo " Awaiting initial approval...\n";
          }

          return $data;

      } catch (RequestException $e) {
          if ($e->hasResponse()) {
              $statusCode = $e->getResponse()->getStatusCode();
              if ($statusCode === 401) {
                  echo "  Unauthorized. Check your Bearer token.\n";
              } elseif ($statusCode === 400) {
                  $body = json_decode($e->getResponse()->getBody(), true);
                  echo "  Bad Request: " . $body['message'] . "\n";
              } else {
                  echo " Request failed: " . $e->getMessage() . "\n";
                  echo $e->getResponse()->getBody() . "\n";
              }
          } else {
              echo " Request failed: " . $e->getMessage() . "\n";
          }
          throw $e;
      }
  }

  getOnboardingStatus($referenceNumber);
  ?>
  ```

  ```java Java theme={null}
  import com.google.gson.Gson;
  import okhttp3.*;

  import java.io.IOException;

  public class GetOnboardingStatus {
      private static final String BEARER_TOKEN = System.getenv("BEARER_TOKEN");
      private static final String REFERENCE_NUMBER = "550e8400-e29b-41d4-a716-446655440000";
      private static final String API_URL = "https://kyc.sbx.moduluslabs.io/v2/onboard/";

      public static void getOnboardingStatus(String refNo) throws IOException {
          OkHttpClient client = new OkHttpClient();

          Request request = new Request.Builder()
                  .url(API_URL + refNo + "/status")
                  .header("Authorization", "Bearer " + BEARER_TOKEN)
                  .header("Accept", "application/json")
                  .get()
                  .build();

          try (Response response = client.newCall(request).execute()) {
              String responseBody = response.body().string();

              if (response.isSuccessful()) {
                  Gson gson = new Gson();
                  StatusResponse data = gson.fromJson(responseBody, StatusResponse.class);

                  System.out.println(" Status retrieved successfully");
                  System.out.println("Merchant: " + data.merchantName);
                  System.out.println("Status: " + data.onboardingStatus);

                  switch (data.onboardingStatus) {
                      case "APPROVED":
                          System.out.println(" Onboarding approved!");
                          break;
                      case "DECLINED":
                          System.out.println("  Declined: " + data.declinedReason);
                          break;
                      case "PENDING":
                          System.out.println(" Awaiting re-approval...");
                          break;
                      default:
                          System.out.println(" Awaiting initial approval...");
                  }
              } else if (response.code() == 401) {
                  System.out.println("  Unauthorized. Check your Bearer token.");
              } else if (response.code() == 400) {
                  System.out.println("  Bad Request: " + responseBody);
              } else {
                  System.out.println(" Request failed: HTTP " + response.code());
                  System.out.println("Details: " + responseBody);
              }
          }
      }

      public static void main(String[] args) {
          try {
              getOnboardingStatus(REFERENCE_NUMBER);
          } catch (IOException e) {
              System.err.println(" Error: " + e.getMessage());
              e.printStackTrace();
          }
      }

      static class StatusResponse {
          String onboardingStatus;
          String onboardingReferenceNumber;
          String merchantName;
          String dateCreated;
          String dateUpdated;
          String declinedReason;
      }
  }
  ```

  ```go Go theme={null}
  package main

  import (
  	"encoding/json"
  	"fmt"
  	"io"
  	"net/http"
  	"os"
  	"time"
  )

  const (
  	apiURL          = "https://kyc.sbx.moduluslabs.io/v2/onboard/"
  	referenceNumber = "550e8400-e29b-41d4-a716-446655440000"
  )

  type StatusResponse struct {
  	OnboardingStatus          string  `json:"onboardingStatus"`
  	OnboardingReferenceNumber string  `json:"onboardingReferenceNumber"`
  	MerchantName              string  `json:"merchantName"`
  	DateCreated               string  `json:"dateCreated"`
  	DateUpdated               *string `json:"dateUpdated"`
  	DeclinedReason            *string `json:"declinedReason"`
  }

  func getOnboardingStatus(refNo string) error {
  	bearerToken := os.Getenv("BEARER_TOKEN")

  	req, err := http.NewRequest("GET", apiURL+refNo+"/status", nil)
  	if err != nil {
  		return fmt.Errorf("failed to create request: %w", err)
  	}

  	req.Header.Set("Authorization", "Bearer "+bearerToken)
  	req.Header.Set("Accept", "application/json")

  	client := &http.Client{Timeout: 30 * time.Second}
  	resp, err := client.Do(req)
  	if err != nil {
  		return fmt.Errorf("request failed: %w", err)
  	}
  	defer resp.Body.Close()

  	body, err := io.ReadAll(resp.Body)
  	if err != nil {
  		return fmt.Errorf("failed to read response: %w", err)
  	}

  	if resp.StatusCode == http.StatusOK {
  		var data StatusResponse
  		if err := json.Unmarshal(body, &data); err != nil {
  			return fmt.Errorf("failed to parse response: %w", err)
  		}

  		fmt.Println(" Status retrieved successfully")
  		fmt.Printf("Merchant: %s\n", data.MerchantName)
  		fmt.Printf("Status: %s\n", data.OnboardingStatus)

  		switch data.OnboardingStatus {
  		case "APPROVED":
  			fmt.Println(" Onboarding approved!")
  		case "DECLINED":
  			if data.DeclinedReason != nil {
  				fmt.Printf("  Declined: %s\n", *data.DeclinedReason)
  			}
  		case "PENDING":
  			fmt.Println(" Awaiting re-approval...")
  		default:
  			fmt.Println(" Awaiting initial approval...")
  		}

  		return nil
  	} else if resp.StatusCode == http.StatusUnauthorized {
  		fmt.Println("  Unauthorized. Check your Bearer token.")
  		return fmt.Errorf("unauthorized")
  	} else if resp.StatusCode == http.StatusBadRequest {
  		fmt.Printf("  Bad Request: %s\n", string(body))
  		return fmt.Errorf("bad request")
  	}

  	fmt.Printf(" Request failed: HTTP %d\n", resp.StatusCode)
  	fmt.Printf("Details: %s\n", string(body))
  	return fmt.Errorf("request failed with status %d", resp.StatusCode)
  }

  func main() {
  	if err := getOnboardingStatus(referenceNumber); err != nil {
  		fmt.Fprintf(os.Stderr, "Error: %v\n", err)
  		os.Exit(1)
  	}
  }
  ```

  ```csharp C# / .NET theme={null}
  using System;
  using System.Net.Http;
  using System.Net.Http.Headers;
  using System.Text.Json;
  using System.Threading.Tasks;

  class Program
  {
      static async Task Main(string[] args)
      {
          await GetOnboardingStatus("550e8400-e29b-41d4-a716-446655440000");
      }

      static async Task GetOnboardingStatus(string refNo)
      {
          var bearerToken = Environment.GetEnvironmentVariable("BEARER_TOKEN");

          if (string.IsNullOrEmpty(bearerToken))
          {
              Console.WriteLine(" Error: BEARER_TOKEN environment variable not set");
              return;
          }

          try
          {
              using var client = new HttpClient();
              client.DefaultRequestHeaders.Authorization =
                  new AuthenticationHeaderValue("Bearer", bearerToken);
              client.DefaultRequestHeaders.Accept.Add(
                  new MediaTypeWithQualityHeaderValue("application/json"));

              var response = await client.GetAsync(
                  $"https://kyc.sbx.moduluslabs.io/v2/onboard/{refNo}/status"
              );

              var responseBody = await response.Content.ReadAsStringAsync();

              if (response.IsSuccessStatusCode)
              {
                  var data = JsonSerializer.Deserialize<StatusResponse>(
                      responseBody,
                      new JsonSerializerOptions { PropertyNameCaseInsensitive = true }
                  );

                  Console.WriteLine(" Status retrieved successfully");
                  Console.WriteLine($"Merchant: {data.MerchantName}");
                  Console.WriteLine($"Status: {data.OnboardingStatus}");

                  switch (data.OnboardingStatus)
                  {
                      case "APPROVED":
                          Console.WriteLine(" Onboarding approved!");
                          break;
                      case "DECLINED":
                          Console.WriteLine($"  Declined: {data.DeclinedReason}");
                          break;
                      case "PENDING":
                          Console.WriteLine(" Awaiting re-approval...");
                          break;
                      default:
                          Console.WriteLine(" Awaiting initial approval...");
                          break;
                  }
              }
              else if ((int)response.StatusCode == 401)
              {
                  Console.WriteLine("  Unauthorized. Check your Bearer token.");
              }
              else if ((int)response.StatusCode == 400)
              {
                  Console.WriteLine($"  Bad Request: {responseBody}");
              }
              else
              {
                  Console.WriteLine($" Request failed: HTTP {(int)response.StatusCode}");
                  Console.WriteLine($"Details: {responseBody}");
              }
          }
          catch (HttpRequestException e)
          {
              Console.WriteLine($" Request failed: {e.Message}");
          }
          catch (Exception e)
          {
              Console.WriteLine($" Error: {e.Message}");
          }
      }
  }

  public class StatusResponse
  {
      public string OnboardingStatus { get; set; }
      public string OnboardingReferenceNumber { get; set; }
      public string MerchantName { get; set; }
      public string DateCreated { get; set; }
      public string DateUpdated { get; set; }
      public string DeclinedReason { get; set; }
  }
  ```
</RequestExample>

## Polling Best Practices

<AccordionGroup>
  <Accordion title="Implement Smart Polling" icon="arrows-rotate">
    Use this lightweight endpoint for efficient status polling:

    ```javascript theme={null}
    async function pollOnboardingStatus(refNo, maxAttempts = 30) {
      let attempts = 0;
      const pollInterval = 30000; // 30 seconds

      const poll = async () => {
        if (attempts >= maxAttempts) {
          console.log('  Polling timeout reached');
          return;
        }

        attempts++;
        console.log(`Checking status (attempt ${attempts}/${maxAttempts})...`);

        try {
          const status = await getOnboardingStatus(refNo);

          if (status.onboardingStatus === 'APPROVED') {
            console.log(' Onboarding approved!');
            return status;
          } else if (status.onboardingStatus === 'DECLINED') {
            console.log(' Onboarding declined');
            return status;
          }

          // Continue polling if NEW or PENDING
          setTimeout(poll, pollInterval);
        } catch (error) {
          console.error('Polling error:', error.message);
          setTimeout(poll, pollInterval);
        }
      };

      await poll();
    }
    ```
  </Accordion>

  <Accordion title="Exponential Backoff" icon="gauge">
    Implement exponential backoff to reduce server load:

    ```javascript theme={null}
    async function pollWithBackoff(refNo, maxAttempts = 20) {
      let attempts = 0;
      let delay = 10000; // Start with 10 seconds
      const maxDelay = 300000; // Max 5 minutes

      const poll = async () => {
        if (attempts >= maxAttempts) return;

        attempts++;
        const status = await getOnboardingStatus(refNo);

        if (['APPROVED', 'DECLINED'].includes(status.onboardingStatus)) {
          return status;
        }

        // Exponential backoff with max delay cap
        delay = Math.min(delay * 1.5, maxDelay);
        setTimeout(poll, delay);
      };

      await poll();
    }
    ```
  </Accordion>

  <Accordion title="Cache Status Results" icon="database">
    Cache the status response to reduce unnecessary API calls:

    ```javascript theme={null}
    const statusCache = new Map();
    const CACHE_TTL = 30000; // 30 seconds

    async function getCachedStatus(refNo) {
      const cached = statusCache.get(refNo);

      if (cached && (Date.now() - cached.timestamp) < CACHE_TTL) {
        return cached.data;
      }

      const status = await getOnboardingStatus(refNo);
      statusCache.set(refNo, {
        data: status,
        timestamp: Date.now()
      });

      return status;
    }
    ```
  </Accordion>

  <Accordion title="Stop Polling on Terminal Status" icon="stop">
    Automatically stop polling when reaching a terminal status:

    ```javascript theme={null}
    const TERMINAL_STATUSES = ['APPROVED', 'DECLINED'];

    async function pollUntilTerminal(refNo) {
      const interval = setInterval(async () => {
        const status = await getOnboardingStatus(refNo);

        if (TERMINAL_STATUSES.includes(status.onboardingStatus)) {
          clearInterval(interval);
          console.log(`Terminal status reached: ${status.onboardingStatus}`);

          // Trigger appropriate action
          if (status.onboardingStatus === 'APPROVED') {
            onApproved(status);
          } else {
            onDeclined(status);
          }
        }
      }, 30000);

      return interval;
    }
    ```
  </Accordion>
</AccordionGroup>

## Use Cases

<CardGroup cols={2}>
  <Card title="Dashboard Status Widget" icon="chart-line">
    Display real-time onboarding status in merchant dashboards
  </Card>

  <Card title="Automated Polling" icon="arrows-rotate">
    Poll for status changes without fetching full onboarding data
  </Card>

  <Card title="Conditional UI Updates" icon="eye">
    Show/hide UI elements based on approval status
  </Card>

  <Card title="Notification Triggers" icon="bell">
    Trigger notifications when status changes to APPROVED or DECLINED
  </Card>
</CardGroup>

## Status Comparison Table

| Status     | Is Approved? | Is Declined? | Is Pending? | Description                                               |
| ---------- | ------------ | ------------ | ----------- | --------------------------------------------------------- |
| `NEW`      | ❌            | ❌            | ❌           | Merchant completed onboarding, awaiting initial review    |
| `PENDING`  | ❌            | ✅ (was)      | ✅           | Merchant updated form after decline, awaiting re-approval |
| `APPROVED` | ✅            | ❌            | ❌           | Merchant has been approved                                |
| `DECLINED` | ❌            | ✅            | ❌           | Merchant was declined, needs to update form               |

## When to Use This Endpoint

<Tabs>
  <Tab title="Use This Endpoint ">
    * Polling for status changes
    * Dashboard status displays
    * Quick status checks
    * Determining if full data fetch is needed
    * Triggering status-based workflows
    * Mobile applications (reduced bandwidth)
  </Tab>

  <Tab title="Use Full Data Endpoint ">
    * Displaying complete merchant information
    * Editing onboarding data
    * Generating reports with full details
    * Viewing bank account information
    * Showing representative details
  </Tab>
</Tabs>

## Troubleshooting

<AccordionGroup>
  <Accordion title="Reference Number Mismatch" icon="triangle-exclamation">
    **Error:** `Onboarding reference number mismatch`

    **Cause:** Trying to access an onboarding record that doesn't belong to your account

    **Solution:**

    * Verify you're using the correct JWT token
    * Each onboarding record can only be accessed by the account that created it
    * Ensure you're not mixing sandbox and production reference numbers
  </Accordion>

  <Accordion title="Polling Never Completes" icon="clock">
    **Issue:** Status remains in NEW or PENDING indefinitely

    **Possible Causes:**

    * Onboarding awaiting manual admin approval
    * Application is in review queue
    * Additional documentation may be required

    **Solution:**

    * Implement maximum polling attempts (recommended: 30-60 minutes)
    * Contact support if status hasn't changed after reasonable time
    * Check for any notifications or emails from the approval team
  </Accordion>

  <Accordion title="Declined Reason Not Showing" icon="question">
    **Issue:** `declinedReason` is `null` even though status is DECLINED

    **Possible Causes:**

    * Admin declined without providing a reason (rare)
    * Data synchronization issue

    **Solution:**

    * Fetch full onboarding data using [Retrieve Onboarding Data](/api-reference/onboarding/retrieve-onboarding-data)
    * Contact support for clarification
  </Accordion>
</AccordionGroup>

## Next Steps

<CardGroup cols={2}>
  <Card title="Retrieve Full Data" icon="database" href="/api-reference/onboarding/retrieve-onboarding-data">
    Get complete onboarding data including all details
  </Card>

  <Card title="Onboard Merchant" icon="plus" href="/api-reference/onboarding/onboard-merchant">
    Create a new merchant onboarding record
  </Card>

  <Card title="Create Account" icon="user-plus" href="/api-reference/onboarding/create-account">
    Create an account before onboarding
  </Card>

  <Card title="Authentication Guide" icon="key" href="/docs/onboarding/authentication">
    Learn about JWT token authentication
  </Card>
</CardGroup>


## OpenAPI

````yaml GET /v2/onboard/{refNo}/status
openapi: 3.1.0
info:
  title: Modulus Labs Onboarding API
  description: API for merchant onboarding, KYC verification, and account management
  version: 2.0.0
servers:
  - url: https://kyc.sbx.moduluslabs.io
    description: Sandbox
security: []
paths:
  /v2/onboard/{refNo}/status:
    get:
      tags:
        - Onboarding
      summary: Retrieve Onboarding Status
      description: >-
        Retrieve the current onboarding status for a merchant using the
        onboarding reference number. This is a lightweight endpoint that returns
        only essential status information.
      operationId: retrieveOnboardingStatus
      parameters:
        - $ref: '#/components/parameters/RefNoParam'
      responses:
        '200':
          description: Onboarding status retrieved successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/OnboardingStatusResponse'
        '400':
          description: Bad Request - Invalid reference number or record does not exist
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
      security:
        - bearerAuth: []
components:
  parameters:
    RefNoParam:
      name: refNo
      in: path
      required: true
      description: The onboarding reference number (UUID v4 format)
      schema:
        type: string
        format: uuid
      example: 550e8400-e29b-41d4-a716-446655440000
  schemas:
    OnboardingStatusResponse:
      type: object
      properties:
        onboardingStatus:
          type: string
          enum:
            - NEW
            - PENDING
            - APPROVED
            - DECLINED
          description: Current status of the merchant's onboarding application
        onboardingReferenceNumber:
          type: string
          format: uuid
          description: Unique identifier for the onboarding record
        merchantName:
          type: string
          description: Trading name or DBA name of the merchant
        dateCreated:
          type: string
          format: date-time
          description: Timestamp when the onboarding record was created
        dateUpdated:
          type: string
          format: date-time
          nullable: true
          description: Timestamp of the most recent status change
        declinedReason:
          type: string
          nullable: true
          description: >-
            Reason provided when declining the application (only present when
            status is DECLINED)
    ErrorResponse:
      type: object
      properties:
        statusCode:
          type: integer
          description: HTTP status code
        message:
          type: string
          description: Error message
        error:
          type: string
          description: Error type
        details:
          type: object
          description: Additional error details
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      bearerFormat: JWT
      description: JWT Bearer token authentication

````