Hello World Sample

Hello World: Building a SuperApp on Ownera Orchestration Layer

This guide walks you through creating your first SuperApp that connects 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.

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  │
│   (SuperApp)    │◄───────►│  Orchestration  │◄───────►│  (Custody, Pay, │
│                 │  REST   │     Layer       │         │   Issuers, etc) │
└─────────────────┘ GraphQL └─────────────────┘         └─────────────────┘

Your SuperApp 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