API Guide: Data Ingestion

This guide details how to ingest key compliance data—Clients, Transactions (Operations), and Alerts—into the Corsa platform using the API.

Full API reference is available in API Reference

1. Ingesting Clients

Corsa supports two types of clients: Individuals and Corporates.

Individual Clients

Individual clients represent natural persons (retail customers). The key data points required for individuals are focused on identity verification:

  • Personal Information: Name, Date of Birth, Gender, Citizenship.
  • Contact Details: Email, Phone Number.
  • Address: Residential address.
  • Documents: Passport, Driver's License, etc. (managed via integration with identity verification vendors).

Corporate Clients

Corporate clients represent legal entities (businesses, organizations). The data structure for corporates is more complex as it involves both entity-level data and related individuals (UBOs):

  • Entity Details: Legal Name, Registration Number, Date of Incorporation.
  • Business Info: Industry, Business Type, Description.
  • Structure: Ownership type, Complexity.
  • Members: Corporate clients can have associated Members (Individual or Corporate) who act as Ultimate Beneficial Owners (UBOs), Directors, or Signatories.

Ingesting Individual Clients

Endpoint: POST /v1/clients/individuals

Use this endpoint to create or update an individual client. You can use the upsert=true query parameter to update an existing client if they already exist (matched by referenceId).

Sample Request:

POST /v1/clients/individuals?upsert=true
Content-Type: application/json

{
  "referenceId": "USER-12345",
  "accountStatus": "APPROVED",
  "activityStatus": "ACTIVE",
  "general": {
    "firstName": "John",
    "lastName": "Doe",
    "dateOfBirth": "1985-06-15",
    "citizenship": "USA",
    "personalId": "123-45-6789",
    "gender": "MALE"
  },
  "address": {
    "addressLine1": "123 Main St",
    "city": "New York",
    "country": "USA",
    "postalCode": "10001"
  },
  "contact": {
    "emailAddress": "[email protected]",
    "phoneNumber": "+1-555-0199"
  },
  "tags": ["retail", "high-volume"]
}

Ingesting Corporate Clients

Endpoint: POST /v1/clients/corporates

Use this endpoint to onboard business entities.

Sample Request:

POST /v1/clients/corporates?upsert=true
Content-Type: application/json

{
  "referenceId": "CORP-98765",
  "accountStatus": "APPROVED",
  "activityStatus": "ACTIVE",
  "general": {
    "legalEntityName": "Acme Innovations LLC",
    "dateOfIncorporation": "2018-04-12",
    "countryOfIncorporation": "USA"
  },
  "business": {
    "industry": "Technology",
    "description": "SaaS provider for financial services",
    "businessType": "FINANCIAL_INSTITUTIONS",
    "incorporationType": "LIMITED_LIABILITY_COMPANY"
  },
  "address": {
    "registrationAddress": {
      "addressLine1": "456 Tech Park Blvd",
      "city": "Austin",
      "country": "USA",
      "postalCode": "73301"
    }
  },
  "tags": ["enterprise", "saas"]
}

2. Ingesting Transactions (Operations)

In Corsa, transactions are ingested as Operations. These can be either Crypto or Fiat transactions. The main operation types are Deposits, Withdrawals, and Trades.

Ingesting a Deposit (Crypto or Fiat)

Endpoint: POST /v1/operations/deposits

Sample Request (Crypto Deposit):

POST /v1/operations/deposits?upsert=true
Content-Type: application/json

{
  "referenceId": "DEP-2024-001",
  "initiatedBy": "123e4567-e89b-12d3-a456-426614174000",
  "initiatedAt": "2024-01-15T08:30:00Z",
  "depositTransaction": {
    "referenceId": "TX-BLOCK-888",
    "txHash": "0x123abc...",
    "amount": {
      "amount": 1.5,
      "currency": "BTC",
      "netAmount": 1.5
    },
    "convertedAmount": {
      "amount": 65000.00,
      "currency": "USD"
    },
    "from": {
      "walletAddress": "0xSourceWalletAddress..."
    },
    "to": {
      "walletAddress": "0xYourDepositAddress..."
    },
    "blockchainNetworkId": "bitcoin-mainnet",
    "statusHistory": [
      {
        "type": "SUCCESS",
        "timestamp": "2024-01-15T08:30:00Z"
      }
    ]
  }
}

Ingesting a Withdrawal (Crypto or Fiat)

Endpoint: POST /v1/operations/withdrawals

Sample Request (Fiat Withdrawal):

POST /v1/operations/withdrawals?upsert=true
Content-Type: application/json

{
  "referenceId": "WDR-FIAT-2024-005",
  "initiatedBy": "123e4567-e89b-12d3-a456-426614174000",
  "initiatedAt": "2024-01-16T14:20:00Z",
  "withdrawTransaction": {
    "referenceId": "TX-BANK-999",
    "amount": {
      "amount": 5000,
      "currency": "USD"
    },
    "to": {
      "bankAccountNumber": "1234567890"
    },
    "paymentMethod": "WIRE_TRANSFER",
    "statusHistory": [
      {
        "type": "PENDING",
        "timestamp": "2024-01-16T14:20:00Z"
      }
    ]
  }
}

Sample Request (Crypto Withdrawal):

POST /v1/operations/withdrawals?upsert=true
Content-Type: application/json

{
  "referenceId": "WDR-2024-005",
  "initiatedBy": "123e4567-e89b-12d3-a456-426614174000",
  "initiatedAt": "2024-01-16T14:20:00Z",
  "withdrawTransaction": {
    "referenceId": "TX-BLOCK-999",
    "txHash": "0x456def...",
    "amount": {
      "amount": 5000,
      "currency": "USDC"
    },
    "to": {
      "walletAddress": "0xDestWalletAddress..."
    },
    "blockchainNetworkId": "ethereum-mainnet",
    "statusHistory": [
      {
        "type": "PENDING",
        "timestamp": "2024-01-16T14:20:00Z"
      }
    ]
  }
}

Ingesting Trades

Trades can be ingested in two ways:

  1. Atomic Ingestion: Sending the entire trade with all its transactions in a single request.
  2. Incremental Ingestion (Fills): Sending parts of the trade (fills) as separate requests that get grouped into the same trade entity.

Endpoint: POST /v1/operations/trades

Atomic Ingestion Example

POST /v1/operations/trades
Content-Type: application/json

{
  "referenceId": "TRADE-100",
  "initiatedBy": "123e4567-e89b-12d3-a456-426614174000",
  "initiatedAt": "2024-01-17T10:00:00Z",
  "tradeType": "BUY",
  "instrumentBaseAsset": "BTC",
  "instrumentQuoteAsset": "USD",
  "price": 60000,
  "quantity": 1,
  "status": "SUCCESS",
  "transactions": [
    {
      "referenceId": "TX-FILL-1",
      "initiatedAt": "2024-01-17T10:00:00Z",
      "amount": { "amount": 0.4, "currency": "BTC" },
      "paymentMethod": "CRYPTO_TRANSFER",
      "statusHistory": [{ "type": "SUCCESS", "timestamp": "2024-01-17T10:00:00Z" }]
    },
    {
      "referenceId": "TX-FILL-2",
      "initiatedAt": "2024-01-17T10:00:05Z",
      "amount": { "amount": 0.6, "currency": "BTC" },
      "paymentMethod": "CRYPTO_TRANSFER",
      "statusHistory": [{ "type": "SUCCESS", "timestamp": "2024-01-17T10:00:05Z" }]
    }
  ]
}

Incremental Ingestion (Fills) Example

Use shouldAppendToExistingTrade=true and the same referenceId to append transactions to an existing trade.

Request 1 (First Fill):

POST /v1/operations/trades?shouldAppendToExistingTrade=true
Content-Type: application/json

{
  "referenceId": "TRADE-100",
  "initiatedBy": "123e4567-e89b-12d3-a456-426614174000",
  "initiatedAt": "2024-01-17T10:00:00Z",
  "tradeType": "BUY",
  "instrumentBaseAsset": "BTC",
  "instrumentQuoteAsset": "USD",
  "price": 60000,
  "quantity": 1,
  "status": "PENDING",
  "transactions": [
    {
      "referenceId": "TX-FILL-1",
      "initiatedAt": "2024-01-17T10:00:00Z",
      "amount": { "amount": 0.4, "currency": "BTC" },
      "paymentMethod": "CRYPTO_TRANSFER",
      "statusHistory": [{ "type": "SUCCESS", "timestamp": "2024-01-17T10:00:00Z" }]
    }
  ]
}

Request 2 (Second Fill):

POST /v1/operations/trades?shouldAppendToExistingTrade=true
Content-Type: application/json

{
  "referenceId": "TRADE-100",
  "initiatedBy": "123e4567-e89b-12d3-a456-426614174000",
  "initiatedAt": "2024-01-17T10:00:00Z",
  "tradeType": "BUY",
  "instrumentBaseAsset": "BTC",
  "instrumentQuoteAsset": "USD",
  "price": 60000,
  "quantity": 1,
  "status": "SUCCESS",
  "transactions": [
    {
      "referenceId": "TX-FILL-2",
      "initiatedAt": "2024-01-17T10:00:05Z",
      "amount": { "amount": 0.6, "currency": "BTC" },
      "paymentMethod": "CRYPTO_TRANSFER",
      "statusHistory": [{ "type": "SUCCESS", "timestamp": "2024-01-17T10:00:05Z" }]
    }
  ]
}

3. Operation & Transaction Statuses

Corsa allows you to track the lifecycle of operations (Deposits, Withdrawals, Trades) and their underlying transactions.

Available Statuses

Operation Statuses (Deposits, Withdrawals, Trades)

  • PENDING: The operation has been initiated but is not yet final.
  • SUCCESS: The operation completed successfully.
  • FAILED: The operation failed.
  • CANCELLED: The operation was cancelled.
  • EXPIRED: The operation expired (specific to time-bound operations).
  • REJECTED: The operation was rejected.

Transaction Statuses

  • PENDING: The transaction is processing.
  • SUCCESS: The transaction was confirmed.
  • FAILED: The transaction failed on-chain or during processing.
  • CANCELLED: The transaction was cancelled.

Updating Statuses

You can update the status of an operation or transaction as it progresses.

Updating a Trade Status

Endpoint: PUT /v1/operations/trades/{id}/updateStatus

Sample Request:

PUT /v1/operations/trades/TRADE-100/updateStatus
Content-Type: application/json

{
  "status": "SUCCESS",
  "timestamp": "2024-01-17T10:05:00Z",
  "reason": "Trade executed successfully",
  "subStatus": "FILLED"
}

Updating a Transaction Status

Endpoint: PUT /v1/transactions/{id}/updateStatus

Sample Request:

PUT /v1/transactions/TX-FILL-1/updateStatus
Content-Type: application/json

{
  "type": "SUCCESS",
  "timestamp": "2024-01-17T10:05:00Z",
  "reason": "Blockchain confirmation received",
  "subStatus": "CONFIRMED_6_BLOCKS"
}

4. Managing Alerts & Cases

Ingesting Alerts

You can push external alerts (e.g., from third-party vendors or internal systems) into Corsa for centralized case management.

Endpoint: POST /v1/alerts

Sample Request:

POST /v1/alerts
Content-Type: application/json

{
  "referenceId": "EXT-ALERT-555",
  "category": "TRANSACTION_MONITORING",
  "priority": "HIGH",
  "status": "NEW",
  "description": "Large withdrawal detected for a high-risk account.",
  "raisedAt": "2024-01-17T09:00:00Z",
  "source": {
    "vendor": "OTHER",
    "vendorAlertId": "INT-SYS-99",
    "alertSource": "API"
  },
  "associatedClients": [
    "123e4567-e89b-12d3-a456-426614174000"
  ],
  "assigneeId": "user-uuid-of-analyst"
}

Note: The assigneeId field is optional. If not provided, the alert will be created without an assignee.

Escalating an Alert to a Case

When an alert requires further investigation or formal review, it can be escalated to a Case. Escalation involves creating a new Case and associating the relevant Alert(s) with it.

Endpoint: POST /v1/cases

Sample Request:

POST /v1/cases
Content-Type: application/json

{
  "referenceId": "CASE-2024-001",
  "priority": "HIGH",
  "category": "TRANSACTION_MONITORING",
  "subCategory": "Suspicious Activity",
  "status": "UNDER_INVESTIGATION",
  "description": "Investigation opened for high-value transaction pattern.",
  "alertsIds": [
    "alert-uuid-from-previous-step"
  ],
  "transactionsIds": [
    "transaction-uuid-related-to-alert"
  ],
  "clientsIds": [
    "client-uuid-related-to-alert"
  ],
  "assigneeId": "analyst-uuid",
  "reviewersIds": [],
  "dueDate": "2024-01-25T17:00:00Z"
}

Note: The reviewersIds field is required but can be an empty array. The case status will be automatically set to NEW upon creation, regardless of what you specify in the request.

This process links the alert to the new case, allowing investigators to track the entire lifecycle from detection (Alert) to resolution (Case).