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:
| Service | Description |
|---|---|
| Custody Service | Simulated custody provider that manages investor keys and signing. Typically configured as custody-service or simulation. |
| Payment Service | Simulated payment/escrow provider that handles fiat currency movements. Typically configured as payment-service or payments-service. |
| Shared Asset | At 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
}| Field | Description |
|---|---|
aud | Your organization ID |
apiKey | Your API key from Ownera |
nonce | 32 bytes: 24 random bytes + 8-byte epoch timestamp (hex encoded) |
iat | Issued-at timestamp (epoch seconds) |
exp | Expiration 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 profile102= Asset profile105= Intent106= 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
| Type | Purpose | Data Fields |
|---|---|---|
ownerInfo | Basic investor info | name, email, type |
KYC/AML | Compliance verification | country, info[] |
Accreditation | Investor accreditation | country, 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
| Status | Description |
|---|---|
Pending | Plan created, waiting for processing |
InProgress | Currently executing |
Completed | Successfully finished |
Failed | Execution failed |
Rejected | Rejected 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
| Concept | Description |
|---|---|
| SuperApp | Your application connected to the Router |
| Resource ID | Global identifier: org:type:uuid |
| FinID | Cryptographic identity (secp256k1 public key) |
| Intent | Business action (primarySale, buyingIntent, sellingIntent, loanIntent) |
| Execution Plan | Multi-party workflow orchestrated by the Router |
| Certificate | Compliance/identity attestation attached to profiles |
Resources
Updated 7 days ago
