Hello World Sample

Hello World: Connecting an Application to the Ownera Router

This guide walks you through connecting a simple application to the Ownera FinP2P Router. By the end, you'll have created an investor profile, linked it to a custody service, and executed a trade.

Terminology: In these guides, SuperApp is shorthand for a Router-connected integration (an Application or Utility/Connector).

Prerequisites

  • Access to an Ownera Router sandbox environment
  • Your credentials from Ownera:
    • API Key - unique identifier for your application
    • Organization ID - your organization identifier on the network
    • Router URL - base URL for your Router instance (e.g., https://router.example.com)
  • An RSA key pair for signing JWT tokens

Sandbox Environment Setup

The following services must be pre-configured in your sandbox environment:

ServiceDescription
Custody ServiceSimulated custody provider that manages investor keys and signing. Typically configured as custody-service or simulation.
Payment ServiceSimulated payment/escrow provider that handles fiat currency movements. Typically configured as payment-service or payments-service.
Shared AssetAt least one asset must be shared with your organization by an issuer. The sandbox typically includes pre-configured test assets with open primary sale intents.

Note: Contact Ownera to provision your sandbox environment with these simulated services and test assets.


Architecture Overview

┌─────────────────┐         ┌─────────────────┐         ┌─────────────────┐
│   Your App      │         │  FinP2P Router  │         │  Other Parties  │
│ (Application)   │◄───────►│  Orchestration  │◄───────►│  (Custody, Pay, │
│                 │  REST   │     Layer       │         │   Issuers, etc) │
└─────────────────┘ GraphQL └─────────────────┘         └─────────────────┘

Your application communicates with the Router via:

  • REST API (/finapi/*) - for transactional actions (create, execute, update)
  • GraphQL API (/graphql) - for queries (assets, positions, execution status)

Step 1: Authentication

Every API request requires a JWT bearer token in the Authorization header.

Token Structure

The JWT consists of three parts: Header, Payload, and Signature.

Header:

{
  "alg": "RS256",
  "typ": "JWT"
}

Payload:

{
  "aud": "<ORGANIZATION_ID>",
  "apiKey": "<API_KEY>",
  "nonce": "<32_BYTES_HEX>",
  "iat": 1701173094,
  "exp": 1701173124
}
FieldDescription
audYour organization ID
apiKeyYour API key from Ownera
nonce32 bytes: 24 random bytes + 8-byte epoch timestamp (hex encoded)
iatIssued-at timestamp (epoch seconds)
expExpiration timestamp (iat + 30 seconds)

Signature:

  • Encode header and payload as base64url
  • Concatenate with . separator
  • Sign with RS256 using your private key

Final Token:

base64url(header).base64url(payload).base64url(signature)

Request Format

POST /finapi/profiles/owner HTTP/1.1
Host: router.example.com
Content-Type: application/json
Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...

{request body}

Important: Each token is single-use. Generate a new token for every API call.


Step 2: Create an Investor (Owner) Profile

Create a global identity for an investor on the FinP2P network.

Request

POST /finapi/profiles/owner
Content-Type: application/json
Authorization: Bearer <token>

{}

Response

{
  "isCompleted": true,
  "response": {
    "id": "your-org:101:edc5c2cc-6688-457a-a0f6-1e7aea28d144"
  },
  "type": "profile"
}

The returned id is the Resource ID - a globally unique identifier with format:

<organization>:<resource-type>:<uuid>
  • 101 = Owner/Investor profile
  • 102 = Asset profile
  • 105 = Intent
  • 106 = Execution Plan

Step 3: Link Investor to Custody Service

Connect the investor to a custody provider to generate their FinID (cryptographic public key identity).

Request

POST /finapi/profiles/owner/{investorId}/account
Content-Type: application/json
Authorization: Bearer <token>

{
  "orgId": "custody-service"
}

Response (Async Operation)

{
  "cid": "e3c59abb-f296-4556-a4c6-9c0e397ad6f9",
  "isCompleted": false,
  "type": "addAccount"
}

Poll for Completion

GET /finapi/operations/status/{cid}
Authorization: Bearer <token>

Completed Response

{
  "cid": "e3c59abb-f296-4556-a4c6-9c0e397ad6f9",
  "isCompleted": true,
  "response": {
    "custodyOrgId": "custody-service",
    "finId": "02d6fea8153a9a64d13e198379dfd4ea3945d03198770e0e5b94f556c4e69f6835"
  },
  "type": "account"
}

The finId is a compressed secp256k1 public key (33 bytes, hex encoded) that identifies the investor cryptographically.


Step 4: Add Compliance Certificates

Attach KYC/AML and accreditation certificates to the investor profile.

Request

POST /finapi/profiles/{investorId}/certificates
Content-Type: application/json
Authorization: Bearer <token>

{
  "type": "ownerInfo",
  "issuanceDate": 1668598695,
  "expirationDate": 1700134695,
  "data": "{\"email\":\"[email protected]\",\"name\":\"John Doe\",\"type\":\"individual\"}"
}

Response

{
  "id": "ec634170-8c62-4d55-b324-0391eb30fac1"
}

Common Certificate Types

TypePurposeData Fields
ownerInfoBasic investor infoname, email, type
KYC/AMLCompliance verificationcountry, info[]
AccreditationInvestor accreditationcountry, info[]

Step 5: Share Investor Profile with Issuer

Before your investor can participate in an asset's primary sale or secondary market, you must share their profile with the issuer's organization.

Request

POST /finapi/profiles/{investorId}/share
Content-Type: application/json
Authorization: Bearer <token>

{
  "organizations": ["issuer-org", "payment-service"]
}

Response

{}

Note: Share the profile with all organizations involved in the trade: the asset issuer, payment service provider, and any other counterparties.


Step 6: Query Available Assets

Use GraphQL to discover assets and open trading intents.

Request

POST /graphql
Content-Type: application/json
Authorization: Bearer <token>

{
  "query": "query { assets { nodes { id name organizationId denomination { code } intents(filter: [{key: \"type\", operator: EQ, value: \"primarySale\"}, {key: \"status\", operator: EQ, value: \"ACTIVE\"}]) { nodes { id type status remainingQuantity intent { ... on PrimarySale { assetTerm { amount } settlementTerm { unitValue asset { ... on FiatAsset { code } } } } } } } } } }"
}

Response

{
  "data": {
    "assets": {
      "nodes": [
        {
          "id": "issuer-org:102:f96240dc-8365-475b-84e2-9611bb5e5bc0",
          "name": "Real Estate Fund A",
          "organizationId": "issuer-org",
          "denomination": { "code": "USD" },
          "intents": {
            "nodes": [
              {
                "id": "issuer-org:105:d07ab369-8bf2-4740-a055-1d11d2edfd38",
                "type": "primarySale",
                "status": "ACTIVE",
                "remainingQuantity": "10000",
                "intent": {
                  "assetTerm": { "amount": "1000" },
                  "settlementTerm": {
                    "unitValue": "2",
                    "asset": { "code": "USD" }
                  }
                }
              }
            ]
          }
        }
      ]
    }
  }
}

Step 7: Execute an Investment

Execute against an open primary sale intent.

Request

POST /finapi/tokens/execute
Content-Type: application/json
Authorization: Bearer <token>

{
  "intentId": "issuer-org:105:d07ab369-8bf2-4740-a055-1d11d2edfd38",
  "user": "your-org:101:edc5c2cc-6688-457a-a0f6-1e7aea28d144",
  "intent": {
    "type": "primarySaleExecution",
    "buyer": "your-org:101:edc5c2cc-6688-457a-a0f6-1e7aea28d144",
    "issuer": "issuer-org:101:3dd376f0-f03b-43dc-8229-134918f62ad9",
    "nonce": "8a03fb05fcd070106d9f346b2abd86a7c72a0bf12acb30f20000000067340000",
    "asset": {
      "term": {
        "asset": {
          "type": "finp2p",
          "resourceId": "issuer-org:102:f96240dc-8365-475b-84e2-9611bb5e5bc0"
        },
        "amount": "1000"
      },
      "instruction": {
        "destinationAccount": {
          "account": {
            "type": "finId",
            "finId": "02d6fea8153a9a64d13e198379dfd4ea3945d03198770e0e5b94f556c4e69f6835",
            "orgId": "issuer-org",
            "custodian": { "orgId": "custody-service" }
          },
          "asset": {
            "type": "finp2p",
            "resourceId": "issuer-org:102:f96240dc-8365-475b-84e2-9611bb5e5bc0"
          }
        }
      }
    },
    "settlement": {
      "term": {
        "asset": { "type": "fiat", "code": "USD" },
        "amount": "2000"
      },
      "instruction": {
        "sourceAccount": {
          "account": {
            "type": "finId",
            "finId": "02d6fea8153a9a64d13e198379dfd4ea3945d03198770e0e5b94f556c4e69f6835",
            "orgId": "payment-service",
            "custodian": { "orgId": "custody-service" }
          },
          "asset": { "type": "fiat", "code": "USD" }
        },
        "destinationAccount": {
          "account": {
            "type": "finId",
            "finId": "03565ed4e4d889ee24b7a6f830aa0df123456789abcdef",
            "orgId": "payment-service",
            "custodian": { "orgId": "custody-service" }
          },
          "asset": { "type": "fiat", "code": "USD" }
        }
      }
    }
  }
}

Response

{
  "isCompleted": true,
  "response": {
    "executionPlanId": "your-org:106:1234567890"
  },
  "type": "execution"
}

Step 8: Monitor Execution Plan

Track the orchestration plan status via GraphQL.

Request

POST /graphql
Content-Type: application/json
Authorization: Bearer <token>

{
  "query": "query { plan(id: \"your-org:106:1234567890\") { id status creationTimestamp intent { id } } }"
}

Response

{
  "data": {
    "plan": {
      "id": "your-org:106:1234567890",
      "status": "Completed",
      "creationTimestamp": 1703495180,
      "intent": {
        "id": "issuer-org:105:d07ab369-8bf2-4740-a055-1d11d2edfd38"
      }
    }
  }
}

Execution Plan Statuses

StatusDescription
PendingPlan created, waiting for processing
InProgressCurrently executing
CompletedSuccessfully finished
FailedExecution failed
RejectedRejected by a party

Complete Integration Flow

┌─────────────────────────────────────────────────────────────────┐
│                     SuperApp Integration Flow                    │
├─────────────────────────────────────────────────────────────────┤
│                                                                  │
│  1. POST /profiles/owner                                         │
│     └─► Create investor → Resource ID                            │
│                                                                  │
│  2. POST /profiles/owner/{id}/account                            │
│     └─► Link custody → FinID (async, poll /operations/status)    │
│                                                                  │
│  3. POST /profiles/{id}/certificates                             │
│     └─► Add KYC/AML certificates                                 │
│                                                                  │
│  4. POST /profiles/{id}/share                                    │
│     └─► Share investor profile with issuer & payment orgs        │
│                                                                  │
│  5. POST /graphql                                                │
│     └─► Query assets & intents (assets must be shared with you)  │
│                                                                  │
│  6. POST /tokens/execute                                         │
│     └─► Execute investment → Execution Plan ID                   │
│                                                                  │
│  7. POST /graphql                                                │
│     └─► Poll plan status until Completed/Failed                  │
│                                                                  │
└─────────────────────────────────────────────────────────────────┘

Key Concepts Reference

ConceptDescription
SuperAppYour application connected to the Router
Resource IDGlobal identifier: org:type:uuid
FinIDCryptographic identity (secp256k1 public key)
IntentBusiness action (primarySale, buyingIntent, sellingIntent, loanIntent)
Execution PlanMulti-party workflow orchestrated by the Router
CertificateCompliance/identity attestation attached to profiles

Resources