@ixo/supamoto-bot-sdk
v0.1.4
Published
An SDK to easily interact with Supamoto bot db
Readme
IXO SUPAMOTO BOT SDK
The IXO Supamoto Bot SDK provides a comprehensive set of database operations for managing customer data, claims, credentials, and other entities in the Supamoto ecosystem. Built with TypeScript and PostgreSQL, it offers type-safe database interactions with built-in utilities for validation, encryption, and indexing.
Table of Contents
- IXO SUPAMOTO BOT SDK
- Table of Contents
- Installation
- Getting Started
- API Client
- Claims Bot Client
- Database Client
- Database Operations
- Customers
- Phones
- Customer Phones
- Claims
- Lead Claims
- Screening Claims
- Contract Claims
- Onboarding Fee Claims
- Home Visit Claims
- Fuel Purchase Claims
- Bag Stove Claims
- Thousand Day Household Claims
- Existing Customer Claims
- Customer Credentials
- Customer Onboardings
- Customer Subscriptions
- Subscriptions
- Unresolved Payments
- Household Entities
- Household Credentials
- IXO Accounts
- Matrix Accounts
- Config
- Claim Issues
- Utilities
- Type Definitions
- Error Handling
- Contributing
- License
- Support
Installation
To install the SDK, add it to your project using npm or yarn:
npm install @ixo/supamoto-bot-sdkor
yarn add @ixo/supamoto-bot-sdkGetting Started
Once installed, import the SDK components into your project:
import { createDatabaseClient, createApiClient, utils } from '@ixo/supamoto-bot-sdk';
import type { IClaim, ICustomer, IPhone, ... } from '@ixo/supamoto-bot-sdk';API Client
The SDK provides an API client for interacting with the Supamoto Bot API endpoints. Create an API client by providing the bot URL and access token:
import { createApiClient } from '@ixo/supamoto-bot-sdk';
const api = createApiClient({
botUrl: 'https://your-bot-url.com',
accessToken: 'your-access-token',
});Claims API
Manage claims through the API:
// Get a specific claim
const claim = await api.claims.v1.getClaim({
claimId: 'claim-123',
collectionId: 'collection-123',
});
// Save a new claim
const savedClaim = await api.claims.v1.saveClaim({
collectionId: 'collection-123',
claim: '{"data": "claim-data"}',
});
// Get claims by collection
const collectionClaims = await api.claims.v1.getCollectionClaims({
collectionId: 'collection-123',
});
// Get claims by multiple collections
const multipleCollectionClaims = await api.claims.v1.getCollectionClaims({
collectionIds: ['collection-123', 'collection-456'],
});
// Get claims by customer
const customerClaims = await api.claims.v1.getCustomerClaims({
customerId: 'C12345678',
});
// Get claims by multiple customers
const multipleCustomerClaims = await api.claims.v1.getCustomerClaims({
customerIds: ['C12345678', 'customer-456'],
});Collection IDs API
Manage collection ID mappings:
// Get all collection IDs
const collectionIds = await api.collectionIds.v1.getCollectionIds();
// Get a specific collection ID by key
const collectionId = await api.collectionIds.v1.getCollectionId({
collectionKey: 'lead', // or 'screening', 'contract', etc.
});
// Set a collection ID for a key
const setResult = await api.collectionIds.v1.setCollectionId({
collectionKey: 'lead',
collectionId: 'collection-123',
});Customers API
Search and retrieve customer data:
// Search customers with filters
const searchResults = await api.customers.v1.searchCustomers({
queue: 'screening',
search: 'John',
country: 'ZM',
queued: true,
limit: 10,
offset: 0,
});
// Get a specific customer
const customer = await api.customers.v1.getCustomer({
customerId: 'C12345678',
});
// Get multiple customers
const customers = await api.customers.v1.getCustomers({
customerIds: ['C12345678', 'customer-456'],
});
// Resolve customer by phone ID (returns array of customer–phone links or null)
const resolved = await api.customers.v1.resolveCustomerByPhoneId({
phoneId: 'phone-123',
});
// Returns: { data: Array<{ customerId, phoneId, isPrimary }> | null }Unresolved Payments API
Create, list, and resolve unresolved payment records (e.g. failed payments that could not be matched to a customer):
// Create an unresolved payment (e.g. when payment matching fails)
const created = await api.unresolvedPayments.v1.createUnresolvedPayment({
traceId: 'trace-123',
telerivetMsgId: 'msg-456',
msisdnHash: 'hash-of-msisdn',
msisdnMd5: 'md5-of-msisdn',
amountFiat: 50.0,
currency: 'ZMW',
timestamp: '2024-01-15T10:30:00Z',
failureReason: 'Customer not found',
// Optional: payerRef, transactionId, provider, country, rawContent
});
// Returns: { data: { id, status } }
// Get unresolved payments with optional filters
const list = await api.unresolvedPayments.v1.getUnresolvedPayments({
status: 'pending', // optional
limit: 20,
offset: 0,
});
// Returns: { data: Array<{ id, status, trace_id, amount_fiat, ... }> }
// Resolve an unresolved payment with a customer
const resolved = await api.unresolvedPayments.v1.resolveUnresolvedPayment({
id: 'payment-record-id',
resolvedCustomerId: 'C12345678',
resolvedBy: 'agent-123',
resolutionNotes: 'Matched via support ticket',
});
// Returns: { data: { id, status } }Override Parameters
All API methods support optional override parameters for bot URL and access token:
// Use different credentials for a specific call
const claim = await api.claims.v1.getClaim(
{
claimId: 'claim-123',
collectionId: 'collection-123',
},
'https://different-bot-url.com', // Override bot URL
'different-access-token', // Override access token
);Claims Bot Client
The SDK provides a specialized claims bot client for submitting various types of claims to the Supamoto Bot system. Create a claims bot client by providing the bot URL and access token:
import { createClaimsBotClient } from '@ixo/supamoto-bot-sdk';
const claimsBot = createClaimsBotClient({
botUrl: 'https://your-bot-url.com',
accessToken: 'your-access-token',
});Claims Submission
Submit different types of claims through the claims bot:
Onboarding Fee Claims
Submit claims for customer onboarding fees:
const onboardingFeeClaim = await claimsBot.claims.v1.submitOnboardingFeeClaim({
customerId: 'C12345678', // Optional if did is provided
did: 'did:ixo:123', // Optional if customerId is provided
country: 'ZM', // Optional: 'ZM' for Zambia, 'MW' for Malawi
currency: 'ZMW', // 'ZMW' for Zambian Kwacha, 'MWK' for Malawian Kwacha
amount: 50.0,
transactionId: 'txn-123456',
method: 'mobile_money', // Optional payment method
provider: 'mtn', // 'mtn', 'zamtel', 'tnm', or 'airtel'
transactionDate: '2024-01-15T10:30:00Z',
});Fuel Purchase Claims
Submit claims for fuel purchases:
const fuelPurchaseClaim = await claimsBot.claims.v1.submitFuelPurchaseClaim({
customerId: 'C12345678',
did: 'did:ixo:123',
country: 'ZM',
currency: 'ZMW',
amount: 25.5,
product: 'Diesel',
quantity: '5 liters',
transactionId: 'txn-789012',
method: 'mobile_money',
provider: 'airtel',
transactionDate: '2024-01-15T14:20:00Z',
});Fuel Delivery Claims
Submit claims for fuel deliveries (uses the same interface as fuel purchase):
const fuelDeliveryClaim = await claimsBot.claims.v1.submitFuelDeliveryClaim({
customerId: 'C12345678',
did: 'did:ixo:123',
country: 'ZM',
currency: 'ZMW',
amount: 30.0,
product: 'Petrol',
quantity: '6 liters',
transactionId: 'txn-345678',
method: 'mobile_money',
provider: 'zamtel',
transactionDate: '2024-01-15T16:45:00Z',
});1000-Day Household Claims
Submit claims for 1000-day-household program:
const householdClaim = await claimsBot.claims.v1.submit1000DayHouseholdClaim({
leadGeneratorId: 'LG-123', // Required
customerId: 'C12345678', // Required
beneficiaryCategory: [BeneficiaryCategory.pregnant], // Required: array of categories
childMaxAge: 18, // Required: Maximum age of child in months
beanIntakeFrequency: BeanIntakeFrequency.daily, // Required: 'None at all', '1-2 times a week', '3-4 times a week', '5-6 times a week', 'Daily'
priceSpecification: '5 ZMW', // Required: Price specification
awarenessIronBeans: AwarenessIronBeans.yes, // Required: 'Yes' or 'No'
knowsNutritionalBenefits: KnowsNutritionalBenefits.yes, // Required: 'Yes' or 'No'
nutritionalBenefitsDetails: [NutritionalBenefitsDetail.ironStatus], // Required: array of: 'iron_status', 'cognitive_support', 'work_capacity', 'high_iron_zinc', 'protein_fiber'
antenatalCardVerified: true, // Required: Boolean indicating if antenatal card is verified
});Lead Creation Claims
Submit claims for lead creation:
const leadClaim = await claimsBot.claims.v1.submitLeadCreationClaim({
customerId: 'C12345678', // Required
nationalId: '123456789', // Optional: National ID of the customer
leadGenerator: LeadGenerator.leadGenerator, // Required: 'Call Center', 'Lead Generator', 'Reseller Shop', 'USSD Signup'
leadGeneratorName: 'John Doe', // Optional: Name of the lead generator (required if leadGenerator is 'Lead Generator')
givenName: 'Jane', // Optional: Customer's first name
familyName: 'Smith', // Optional: Customer's last name
telephone: '+260123456789', // Optional: Customer's telephone number
});Collection IDs Management
Retrieve collection ID mappings for different claim types:
// Get all collection IDs
const allCollectionIds = await claimsBot.collectionIds.v1.getCollectionIds();
// Returns: { lead, screening, contract, onboardingFee, homeVisit, fuelPurchase, bagStove, 1000DayHousehold }
// Get a specific collection ID
const onboardingFeeCollectionId = await claimsBot.collectionIds.v1.getCollectionId({
collectionKey: 'onboardingFee', // 'lead', 'screening', 'contract', 'onboardingFee', 'homeVisit', 'fuelPurchase', 'bagStove', '1000DayHousehold'
});Supported Enums
The claims bot client supports several enums for type safety:
import {
Country,
Currency,
PaymentProvider,
BeneficiaryCategory,
BeanIntakeFrequency,
AwarenessIronBeans,
KnowsNutritionalBenefits,
NutritionalBenefitsDetail,
LeadGenerator,
} from '@ixo/supamoto-bot-sdk';
// Countries
Country.Zambia; // 'ZM'
Country.Malawi; // 'MW'
// Currencies
Currency.ZambianKwacha; // 'ZMW'
Currency.MalawianKwacha; // 'MWK'
// Payment Providers
PaymentProvider.MTN; // 'mtn'
PaymentProvider.ZAMTEL; // 'zamtel'
PaymentProvider.TNM; // 'tnm'
PaymentProvider.AIRTEL; // 'airtel'
// Beneficiary Categories (for 1000-day household claims)
BeneficiaryCategory.pregnant; // 'Pregnant Woman'
BeneficiaryCategory.breastfeeding; // 'Breastfeeding Woman'
BeneficiaryCategory.child; // 'Chile Below 2 Years'
// Bean Intake Frequency
BeanIntakeFrequency.none; // 'None at all'
BeanIntakeFrequency.oneOrTwo; // '1-2 times a week'
BeanIntakeFrequency.threeOrFour; // '3-4 times a week'
BeanIntakeFrequency.fiveOrSix; // '5-6 times a week'
BeanIntakeFrequency.daily; // 'Daily'
// Awareness Iron Beans
AwarenessIronBeans.yes; // 'Yes'
AwarenessIronBeans.no; // 'No'
// Knows Nutritional Benefits
KnowsNutritionalBenefits.yes; // 'Yes'
KnowsNutritionalBenefits.no; // 'No'
// Nutritional Benefits Details
NutritionalBenefitsDetail.ironStatus; // 'iron_status'
NutritionalBenefitsDetail.cognitiveSupport; // 'cognitive_support'
NutritionalBenefitsDetail.workCapacity; // 'work_capacity'
NutritionalBenefitsDetail.highIronZinc; // 'high_iron_zinc'
NutritionalBenefitsDetail.protein_fiber; // 'protein_fiber'
// Lead Generators (for lead creation claims)
LeadGenerator.callCenter; // 'Call Center'
LeadGenerator.leadGenerator; // 'Lead Generator'
LeadGenerator.resellerShop; // 'Reseller Shop'
LeadGenerator.ussdSignup; // 'USSD Signup'Response Types
All claim submission methods return a response with the generated claim ID:
interface ClaimSubmissionResponse {
data: {
claimId: string;
};
}Error Handling
The claims bot client throws exceptions for API errors. Always wrap operations in try-catch blocks:
try {
const claim = await claimsBot.claims.v1.submit1000DayHouseholdClaim({
leadGeneratorId: '123',
customerId: 'C12345678',
beneficiaryCategory: [BeneficiaryCategory.pregnant],
childMaxAge: 18,
beanIntakeFrequency: BeanIntakeFrequency.daily,
priceSpecification: 'Standard pricing',
awarenessIronBeans: AwarenessIronBeans.yes,
knowsNutritionalBenefits: KnowsNutritionalBenefits.yes,
nutritionalBenefitsDetails: [NutritionalBenefitsDetail.ironStatus],
antenatalCardVerified: true,
});
console.log('Claim submitted:', claim.data.claimId);
} catch (error) {
console.error('Claims bot error:', error);
// Handle the error appropriately
}Database Client
Create a database client by providing PostgreSQL connection parameters and an encryption key (base64-encoded string):
import { createDatabaseClient } from '@ixo/supamoto-bot-sdk';
const db = createDatabaseClient(
{
user: 'your-username',
password: 'your-password',
host: 'localhost',
database: 'supamoto_db',
port: 5432,
ssl: false,
},
'your-base64-encryption-key', // Base64-encoded encryption key
);
// Use the client
const customer = await db.customers.v1.selectCustomer({
customerId: 'C12345678',
});Connection Pool Configuration
The SDK uses PostgreSQL connection pooling. You can configure the pool with these options:
const db = createDatabaseClient(
{
// Connection parameters
user: 'username',
password: 'password',
host: 'localhost',
database: 'supamoto_db',
port: 5432,
connectionString: 'postgresql://user:password@localhost:5432/db', // Alternative to individual params
// Pool configuration
max: 20, // maximum number of clients in the pool
min: 5, // minimum number of clients in the pool
idleTimeoutMillis: 30000, // how long a client is allowed to remain idle
connectionTimeoutMillis: 2000, // timeout when connecting a new client
// SSL configuration
ssl: {
rejectUnauthorized: false,
},
},
'your-base64-encryption-key', // Base64-encoded encryption key for data encryption/decryption
);Transactions
The SDK supports database transactions:
await db.transaction(async (client) => {
// All operations within this block use the same transaction
const customer = await db.customers.v1.insertCustomer(
{
customerId: 'C12345678',
status: 'active',
},
client,
);
const phone = await db.phones.v1.insertPhone(
{
id: 'phone-123',
phoneNumber: Buffer.from('+1234567890'),
},
client,
);
await db.customerPhones.v1.insertCustomerPhone(
{
customerId: customer.customer_id,
phoneId: phone.id,
isPrimary: true,
},
client,
);
});Database Operations
Customers
Manage customer data with encrypted personal information:
// Select customers
const customer = await db.customers.v1.selectCustomer({
customerId: 'C12345678',
});
const customers = await db.customers.v1.selectCustomers({
customerIds: ['C12345678', 'customer-456'],
});
// Insert customer
const newCustomer = await db.customers.v1.insertCustomer({
customerId: 'C12345678',
fullName: Buffer.from('John Doe'),
email: Buffer.from('[email protected]'),
encryptedPin: Buffer.from('encrypted-pin'),
nationalId: Buffer.from('123456789'),
preferredLanguage: 'en',
address: Buffer.from('123 Main St'),
nameIndex: [Buffer.from('john'), Buffer.from('doe')],
addressIndex: [Buffer.from('123'), Buffer.from('main')],
countryIndex: 'US',
givenName: Buffer.from('John'),
familyName: Buffer.from('Doe'),
profileImage: Buffer.from('image-data'),
status: 'active',
errorMessage: undefined,
});
// Update customer
const updatedCustomer = await db.customers.v1.updateCustomer({
customerId: 'C12345678',
status: 'inactive',
errorMessage: 'Account suspended',
});
// Delete customer
const deletedCustomer = await db.customers.v1.deleteCustomer({
customerId: 'C12345678',
});Phones
Manage phone number data:
// Select phones
const phone = await db.phones.v1.selectPhone({ id: 'phone-123' });
const phones = await db.phones.v1.selectPhones({ ids: ['phone-123', 'phone-456'] });
// Insert phone
const newPhone = await db.phones.v1.insertPhone({
id: 'phone-123',
phoneNumber: Buffer.from('+1234567890'),
firstSeen: '2024-01-01T00:00:00Z',
lastSeen: '2024-01-15T12:00:00Z',
numberOfVisits: 5,
});
// Update phone
const updatedPhone = await db.phones.v1.updatePhoneLastSeen({
id: 'phone-123',
lastSeen: '2024-01-20T15:30:00Z',
});
const updatedVisits = await db.phones.v1.updatePhoneNumberOfVisits({
id: 'phone-123',
numberOfVisits: 10,
});
// General update
const updated = await db.phones.v1.updatePhone({
id: 'phone-123',
lastSeen: '2024-01-20T15:30:00Z',
numberOfVisits: 10,
});
// Delete phone
const deletedPhone = await db.phones.v1.deletePhone({ id: 'phone-123' });Customer Phones
Link customers to their phone numbers:
// Select customer phones
const customerPhone = await db.customerPhones.v1.selectCustomerPhone({
id: 'cp-123',
});
const customerPhones = await db.customerPhones.v1.selectCustomerPhones({
ids: ['cp-123', 'cp-456'],
});
const phonesByCustomer = await db.customerPhones.v1.selectCustomerPhonesByCustomerId({
customerId: 'C12345678',
});
const customersByPhone = await db.customerPhones.v1.selectCustomerPhonesByPhoneId({
phoneId: 'phone-123',
});
// Insert customer phone
const newCustomerPhone = await db.customerPhones.v1.insertCustomerPhone({
customerId: 'C12345678',
phoneId: 'phone-123',
isPrimary: true,
});
// Update primary status
const updatedPrimary = await db.customerPhones.v1.updateCustomerPhoneIsPrimary({
id: 'cp-123',
isPrimary: false,
});
// Delete customer phone
const deleted = await db.customerPhones.v1.deleteCustomerPhone({ id: 'cp-123' });Claims
Manage claim data and status:
// Select claims
const claim = await db.claims.v1.selectClaim({ claimId: 'claim-123' });
const claims = await db.claims.v1.selectClaims({ claimIds: ['claim-123', 'claim-456'] });
const claimsByCustomer = await db.claims.v1.selectClaimsByCustomerId({
customerId: 'C12345678',
});
const claimsByCollection = await db.claims.v1.selectClaimsByCollectionId({
collectionId: 'collection-123',
});
const claimsByStatus = await db.claims.v1.selectClaimsByStatus({ status: 'pending' });
const claimsByCustomerAndCollection = await db.claims.v1.selectClaimsByCustomerIdAndCollectionId({
customerId: 'C12345678',
collectionId: 'collection-123',
});
// Insert claim
const newClaim = await db.claims.v1.insertClaim({
claimId: 'claim-123',
customerId: 'C12345678',
collectionId: 'collection-123',
status: 'pending',
});
// Upsert claim
const upsertedClaim = await db.claims.v1.upsertClaim({
claimId: 'claim-123',
customerId: 'C12345678',
collectionId: 'collection-123',
status: 'approved',
});
// Update claim
const updatedStatus = await db.claims.v1.updateClaimStatus({
claimId: 'claim-123',
status: 'approved',
});
const updatedCustomer = await db.claims.v1.updateClaimCustomerId({
claimId: 'claim-123',
customerId: 'customer-456',
});
// Delete claim
const deletedClaim = await db.claims.v1.deleteClaim({ claimId: 'claim-123' });Lead Claims
Manage lead claim data:
// Select lead claims
const leadClaim = await db.leadClaims.v1.selectLeadClaim({ claimId: 'claim-123' });
const leadClaims = await db.leadClaims.v1.selectLeadClaims({ claimIds: ['claim-123', 'claim-456'] });
const leadClaimsByCustomer = await db.leadClaims.v1.selectLeadClaimsByCustomerId({
customerId: 'C12345678',
});
// Insert lead claim
const newLeadClaim = await db.leadClaims.v1.insertLeadClaim({
claimId: 'claim-123',
customerId: 'C12345678',
nationalId: '123456789',
leadGenerator: 'Lead Generator',
nameLeadGenerator: 'John Doe',
givenName: 'Jane',
familyName: 'Smith',
telephone: '+260123456789',
});
// Upsert lead claim
const upsertedLeadClaim = await db.leadClaims.v1.upsertLeadClaim({
claimId: 'claim-123',
customerId: 'C12345678',
nationalId: '123456789',
leadGenerator: 'Call Center',
});
// Update lead claim
const updatedLeadClaim = await db.leadClaims.v1.updateLeadClaim({
claimId: 'claim-123',
telephone: '+260987654321',
});
// Delete lead claim
const deletedLeadClaim = await db.leadClaims.v1.deleteLeadClaim({ claimId: 'claim-123' });Screening Claims
Manage screening claim data:
// Select screening claims
const screeningClaim = await db.screeningClaims.v1.selectScreeningClaim({ claimId: 'claim-123' });
const screeningClaims = await db.screeningClaims.v1.selectScreeningClaims({ claimIds: ['claim-123', 'claim-456'] });
const screeningClaimsByCustomer = await db.screeningClaims.v1.selectScreeningClaimsByCustomerId({
customerId: 'C12345678',
});
// Insert screening claim
const newScreeningClaim = await db.screeningClaims.v1.insertScreeningClaim({
claimId: 'claim-123',
customerId: 'C12345678',
nationalId: '123456789',
givenName: 'Jane',
familyName: 'Smith',
telephone: '+260123456789',
addressCountry: 'ZM',
signingContract: true,
});
// Upsert screening claim
const upsertedScreeningClaim = await db.screeningClaims.v1.upsertScreeningClaim({
claimId: 'claim-123',
customerId: 'C12345678',
signingContract: true,
});
// Update screening claim
const updatedScreeningClaim = await db.screeningClaims.v1.updateScreeningClaim({
claimId: 'claim-123',
membershipFeeWilling: true,
});
// Delete screening claim
const deletedScreeningClaim = await db.screeningClaims.v1.deleteScreeningClaim({ claimId: 'claim-123' });Contract Claims
Manage contract claim data:
// Select contract claims
const contractClaim = await db.contractClaims.v1.selectContractClaim({ claimId: 'claim-123' });
const contractClaims = await db.contractClaims.v1.selectContractClaims({ claimIds: ['claim-123', 'claim-456'] });
const contractClaimsByCustomer = await db.contractClaims.v1.selectContractClaimsByCustomerId({
customerId: 'C12345678',
});
// Insert contract claim
const newContractClaim = await db.contractClaims.v1.insertContractClaim({
claimId: 'claim-123',
customerId: 'C12345678',
signedContract: 'contract-data',
signedContractDate: '2024-01-15',
nationalId: '123456789',
profileImage: 'image-data',
});
// Upsert contract claim
const upsertedContractClaim = await db.contractClaims.v1.upsertContractClaim({
claimId: 'claim-123',
customerId: 'C12345678',
signedContract: 'updated-contract-data',
});
// Update contract claim
const updatedContractClaim = await db.contractClaims.v1.updateContractClaim({
claimId: 'claim-123',
profileImage: 'new-image-data',
});
// Delete contract claim
const deletedContractClaim = await db.contractClaims.v1.deleteContractClaim({ claimId: 'claim-123' });Onboarding Fee Claims
Manage onboarding fee claim data:
// Select onboarding fee claims
const onboardingFeeClaim = await db.onboardingFeeClaims.v1.selectOnboardingFeeClaim({ claimId: 'claim-123' });
const onboardingFeeClaims = await db.onboardingFeeClaims.v1.selectOnboardingFeeClaims({
claimIds: ['claim-123', 'claim-456'],
});
const onboardingFeeClaimsByCustomer = await db.onboardingFeeClaims.v1.selectOnboardingFeeClaimsByCustomerId({
customerId: 'C12345678',
});
// Insert onboarding fee claim
const newOnboardingFeeClaim = await db.onboardingFeeClaims.v1.insertOnboardingFeeClaim({
claimId: 'claim-123',
customerId: 'C12345678',
did: 'did:ixo:123',
country: 'ZM',
currency: 'ZMW',
monetaryAmount: 50.0,
transactionId: 'txn-123456',
paymentMethod: 'mobile_money',
paymentProvider: 'mtn',
dateTime: '2024-01-15T10:30:00Z',
});
// Upsert onboarding fee claim
const upsertedOnboardingFeeClaim = await db.onboardingFeeClaims.v1.upsertOnboardingFeeClaim({
claimId: 'claim-123',
customerId: 'C12345678',
monetaryAmount: 50.0,
transactionId: 'txn-123456',
});
// Update onboarding fee claim
const updatedOnboardingFeeClaim = await db.onboardingFeeClaims.v1.updateOnboardingFeeClaim({
claimId: 'claim-123',
monetaryAmount: 60.0,
});
// Delete onboarding fee claim
const deletedOnboardingFeeClaim = await db.onboardingFeeClaims.v1.deleteOnboardingFeeClaim({ claimId: 'claim-123' });Home Visit Claims
Manage home visit claim data:
// Select home visit claims
const homeVisitClaim = await db.homeVisitClaims.v1.selectHomeVisitClaim({ claimId: 'claim-123' });
const homeVisitClaims = await db.homeVisitClaims.v1.selectHomeVisitClaims({ claimIds: ['claim-123', 'claim-456'] });
const homeVisitClaimsByCustomer = await db.homeVisitClaims.v1.selectHomeVisitClaimsByCustomerId({
customerId: 'C12345678',
});
// Insert home visit claim
const newHomeVisitClaim = await db.homeVisitClaims.v1.insertHomeVisitClaim({
claimId: 'claim-123',
customerId: 'C12345678',
geoCoordinates: '-15.4167,28.2833',
stovePhoto: 'photo-data',
comment: 'Home visit completed successfully',
});
// Upsert home visit claim
const upsertedHomeVisitClaim = await db.homeVisitClaims.v1.upsertHomeVisitClaim({
claimId: 'claim-123',
customerId: 'C12345678',
geoCoordinates: '-15.4167,28.2833',
});
// Update home visit claim
const updatedHomeVisitClaim = await db.homeVisitClaims.v1.updateHomeVisitClaim({
claimId: 'claim-123',
comment: 'Updated comment',
});
// Delete home visit claim
const deletedHomeVisitClaim = await db.homeVisitClaims.v1.deleteHomeVisitClaim({ claimId: 'claim-123' });Fuel Purchase Claims
Manage fuel purchase claim data:
// Select fuel purchase claims
const fuelPurchaseClaim = await db.fuelPurchaseClaims.v1.selectFuelPurchaseClaim({ claimId: 'claim-123' });
const fuelPurchaseClaims = await db.fuelPurchaseClaims.v1.selectFuelPurchaseClaims({
claimIds: ['claim-123', 'claim-456'],
});
const fuelPurchaseClaimsByCustomer = await db.fuelPurchaseClaims.v1.selectFuelPurchaseClaimsByCustomerId({
customerId: 'C12345678',
});
// Insert fuel purchase claim
const newFuelPurchaseClaim = await db.fuelPurchaseClaims.v1.insertFuelPurchaseClaim({
claimId: 'claim-123',
customerId: 'C12345678',
did: 'did:ixo:123',
product: 'Diesel',
quantity: '5 liters',
currency: 'ZMW',
monetaryAmount: 25.5,
transactionId: 'txn-789012',
paymentMethod: 'mobile_money',
paymentProvider: 'airtel',
dateTime: '2024-01-15T14:20:00Z',
});
// Upsert fuel purchase claim
const upsertedFuelPurchaseClaim = await db.fuelPurchaseClaims.v1.upsertFuelPurchaseClaim({
claimId: 'claim-123',
customerId: 'C12345678',
product: 'Diesel',
quantity: '5 liters',
monetaryAmount: 25.5,
});
// Update fuel purchase claim
const updatedFuelPurchaseClaim = await db.fuelPurchaseClaims.v1.updateFuelPurchaseClaim({
claimId: 'claim-123',
quantity: '6 liters',
monetaryAmount: 30.0,
});
// Delete fuel purchase claim
const deletedFuelPurchaseClaim = await db.fuelPurchaseClaims.v1.deleteFuelPurchaseClaim({ claimId: 'claim-123' });Bag Stove Claims
Manage bag stove claim data:
// Select bag stove claims
const bagStoveClaim = await db.bagStoveClaims.v1.selectBagStoveClaim({ claimId: 'claim-123' });
const bagStoveClaims = await db.bagStoveClaims.v1.selectBagStoveClaims({ claimIds: ['claim-123', 'claim-456'] });
const bagStoveClaimsByCustomer = await db.bagStoveClaims.v1.selectBagStoveClaimsByCustomerId({
customerId: 'C12345678',
});
// Insert bag stove claim
const newBagStoveClaim = await db.bagStoveClaims.v1.insertBagStoveClaim({
claimId: 'claim-123',
customerId: 'C12345678',
quantity: '2',
distributionStoveId: 'stove-123',
registerCustomer: true,
});
// Upsert bag stove claim
const upsertedBagStoveClaim = await db.bagStoveClaims.v1.upsertBagStoveClaim({
claimId: 'claim-123',
customerId: 'C12345678',
quantity: '2',
});
// Update bag stove claim
const updatedBagStoveClaim = await db.bagStoveClaims.v1.updateBagStoveClaim({
claimId: 'claim-123',
quantity: '3',
});
// Delete bag stove claim
const deletedBagStoveClaim = await db.bagStoveClaims.v1.deleteBagStoveClaim({ claimId: 'claim-123' });Thousand Day Household Claims
Manage 1000-day household claim data:
// Select thousand day household claims
const thousandDayClaim = await db.thousandDayHouseholdClaims.v1.selectThousandDayHouseholdClaim({
claimId: 'claim-123',
});
const thousandDayClaims = await db.thousandDayHouseholdClaims.v1.selectThousandDayHouseholdClaims({
claimIds: ['claim-123', 'claim-456'],
});
const thousandDayClaimsByCustomer = await db.thousandDayHouseholdClaims.v1.selectThousandDayHouseholdClaimsByCustomerId(
{
customerId: 'C12345678',
},
);
// Insert thousand day household claim
const newThousandDayClaim = await db.thousandDayHouseholdClaims.v1.insertThousandDayHouseholdClaim({
claimId: 'claim-123',
customerId: 'C12345678',
leadGeneratorId: 'LG-123',
beneficiaryCategory: 'Pregnant Woman',
childMaxAge: 18,
beanIntakeFrequency: 'Daily',
priceSpecification: '5 ZMW',
awarenessIronBeans: true,
knowsNutritionalBenefits: true,
nutritionalBenefitDetails: 'iron_status',
confirmActionAntenatalCardVerified: true,
});
// Upsert thousand day household claim
const upsertedThousandDayClaim = await db.thousandDayHouseholdClaims.v1.upsertThousandDayHouseholdClaim({
claimId: 'claim-123',
customerId: 'C12345678',
leadGeneratorId: 'LG-123',
beneficiaryCategory: 'Pregnant Woman',
});
// Update thousand day household claim
const updatedThousandDayClaim = await db.thousandDayHouseholdClaims.v1.updateThousandDayHouseholdClaim({
claimId: 'claim-123',
beanIntakeFrequency: '5-6 times a week',
});
// Delete thousand day household claim
const deletedThousandDayClaim = await db.thousandDayHouseholdClaims.v1.deleteThousandDayHouseholdClaim({
claimId: 'claim-123',
});Existing Customer Claims
Manage existing customer claim data:
// Select existing customer claims
const existingCustomerClaim = await db.existingCustomerClaims.v1.selectExistingCustomerClaim({ claimId: 'claim-123' });
const existingCustomerClaims = await db.existingCustomerClaims.v1.selectExistingCustomerClaims({
claimIds: ['claim-123', 'claim-456'],
});
const existingCustomerClaimsByCustomer = await db.existingCustomerClaims.v1.selectExistingCustomerClaimsByCustomerId({
customerId: 'C12345678',
});
// Insert existing customer claim
const newExistingCustomerClaim = await db.existingCustomerClaims.v1.insertExistingCustomerClaim({
claimId: 'claim-123',
customerId: 'C12345678',
clientType: 'returning',
givenName: 'Jane',
familyName: 'Smith',
nationalId: '123456789',
telephone: '+260123456789',
deliveryMethod: 'home_delivery',
addressCountry: 'ZM',
});
// Upsert existing customer claim
const upsertedExistingCustomerClaim = await db.existingCustomerClaims.v1.upsertExistingCustomerClaim({
claimId: 'claim-123',
customerId: 'C12345678',
clientType: 'returning',
});
// Update existing customer claim
const updatedExistingCustomerClaim = await db.existingCustomerClaims.v1.updateExistingCustomerClaim({
claimId: 'claim-123',
deliveryMethod: 'pickup',
});
// Delete existing customer claim
const deletedExistingCustomerClaim = await db.existingCustomerClaims.v1.deleteExistingCustomerClaim({
claimId: 'claim-123',
});Customer Credentials
Manage customer credential data:
// Select credentials
const credential = await db.customerCredentials.v1.selectCustomerCredential({ id: 1 });
const credentials = await db.customerCredentials.v1.selectCustomerCredentials({ ids: [1, 2] });
const credentialsByCustomer = await db.customerCredentials.v1.selectCustomerCredentialsByCustomerId({
customerId: 'C12345678',
});
const credentialsByStatus = await db.customerCredentials.v1.selectCustomerCredentialsByStatus({
status: 'issued',
});
// Insert credential
const newCredential = await db.customerCredentials.v1.insertCustomerCredential({
customerId: 'C12345678',
status: 'pending',
});
// Update credential
const updatedCredential = await db.customerCredentials.v1.updateCustomerCredential({
id: 1,
status: 'issued',
});
// Delete credential
const deletedCredential = await db.customerCredentials.v1.deleteCustomerCredential({ id: 1 });Customer Onboardings
Track customer onboarding progress:
// Select onboardings
const onboarding = await db.customerOnboardings.v1.selectCustomerOnboarding({
customerId: 'C12345678',
});
const onboardings = await db.customerOnboardings.v1.selectCustomerOnboardings({
customerIds: ['C12345678', 'customer-456'],
});
const onboardingsByStatus = await db.customerOnboardings.v1.selectCustomerOnboardingsByStatus({
status: 'lead',
});
// Insert onboarding
const newOnboarding = await db.customerOnboardings.v1.insertCustomerOnboarding({
customerId: 'C12345678',
status: 'lead',
});
// Update onboarding
const updatedOnboarding = await db.customerOnboardings.v1.updateCustomerOnboarding({
id: 1,
status: 'complete',
});
// Delete onboarding
const deletedOnboarding = await db.customerOnboardings.v1.deleteCustomerOnboarding({ id: 1 });Customer Subscriptions
Manage customer subscription data:
// Select customer subscriptions
const customerSubscription = await db.customerSubscriptions.v1.selectCustomerSubscription({
id: 'subscription-123',
});
const customerSubscriptions = await db.customerSubscriptions.v1.selectCustomerSubscriptions({
ids: ['subscription-123', 'subscription-456'],
});
const subscriptionsByCustomer = await db.customerSubscriptions.v1.selectCustomerSubscriptionsByCustomerId({
customerId: 'C12345678',
});Subscriptions
Manage subscription data:
// Select subscriptions
const subscription = await db.subscriptions.v1.selectSubscription({ id: 'subscription-123' });
const subscriptions = await db.subscriptions.v1.selectSubscriptions({
ids: ['subscription-123', 'subscription-456'],
});
const subscriptionsByCustomer = await db.subscriptions.v1.selectSubscriptionsByCustomerId({
customerId: 'C12345678',
});Unresolved Payments
Manage unresolved payment records (payments that could not be matched to a customer at ingestion time):
// Insert unresolved payment
const newPayment = await db.unresolvedPayments.v1.insertUnresolvedPayment({
traceId: 'trace-123',
telerivetMsgId: 'msg-456',
msisdnHash: 'hash-of-msisdn',
msisdnMd5: 'md5-of-msisdn',
amountFiat: 50.0,
currency: 'ZMW',
paymentTimestamp: '2024-01-15T10:30:00Z',
failureReason: 'Customer not found',
// Optional: payerRef, transactionId, provider, country, rawContent
});
// Select unresolved payments with optional filters
const payments = await db.unresolvedPayments.v1.selectUnresolvedPayments({
status: 'pending',
limit: 50,
offset: 0,
});
// Update resolution (link payment to a customer)
await db.unresolvedPayments.v1.updateUnresolvedPaymentResolution({
id: 'payment-record-id',
resolvedCustomerId: 'C12345678',
resolvedBy: 'agent-123',
resolutionNotes: 'Matched via support',
});Household Entities
Manage household entity data:
// Select household entities
const entity = await db.householdEntities.v1.selectHouseholdEntity({
did: 'did:ixo:entity:123',
});
const entities = await db.householdEntities.v1.selectHouseholdEntities({
dids: ['did:ixo:entity:123', 'did:ixo:entity:456'],
});
const entitiesByIxoAccount = await db.householdEntities.v1.selectHouseholdEntitiesByIxoAccount({
ixoAccount: 'ixo1abc123',
});
// Insert entity
const newEntity = await db.householdEntities.v1.insertHouseholdEntity({
did: 'did:ixo:entity:123',
ixoAccount: 'ixo1abc123',
matrixRoomId: '!room123:matrix.org',
});
// Update entity
const updatedEntity = await db.householdEntities.v1.updateHouseholdEntity({
did: 'did:ixo:entity:123',
ixoAccount: 'ixo1abc123',
matrixRoomId: '!room456:matrix.org',
});
// Delete entity
const deletedEntity = await db.householdEntities.v1.deleteHouseholdEntity({
did: 'did:ixo:entity:123',
});Household Credentials
Manage household credential data:
// Select credentials
const credential = await db.householdCredentials.v1.selectHouseholdCredential({ id: 1 });
const credentials = await db.householdCredentials.v1.selectHouseholdCredentials({ ids: [1, 2] });
const credentialsByEntity = await db.householdCredentials.v1.selectHouseholdCredentialsByHouseholdEntityDid({
householdEntityDid: 'did:ixo:entity:123',
});
// Insert credential
const newCredential = await db.householdCredentials.v1.insertHouseholdCredential({
householdEntityDid: 'did:ixo:entity:123',
status: 'pending',
});
// Update credential
const updatedCredential = await db.householdCredentials.v1.updateHouseholdCredential({
id: 1,
status: 'issued',
});
// Delete credential
const deletedCredential = await db.householdCredentials.v1.deleteHouseholdCredential({ id: 1 });IXO Accounts
Manage IXO blockchain accounts:
// Select accounts
const account = await db.ixoAccounts.v1.selectIxoAccount({
address: 'ixo1abc123',
});
const accounts = await db.ixoAccounts.v1.selectIxoAccounts({
addresses: ['ixo1abc123', 'ixo1def456'],
});
const accountsByCustomer = await db.ixoAccounts.v1.selectIxoAccountsByCustomerId({
customerId: 'C12345678',
});
const accountsByDid = await db.ixoAccounts.v1.selectIxoAccountsByDid({
did: 'did:ixo:123',
});
// Insert account
const newAccount = await db.ixoAccounts.v1.insertIxoAccount({
address: 'ixo1abc123',
did: 'did:ixo:123',
customerId: 'C12345678',
encryptedMnemonic: 'encrypted-mnemonic',
isPrimary: true,
});
// Update account
const updatedAccount = await db.ixoAccounts.v1.updateIxoAccount({
address: 'ixo1abc123',
did: 'did:ixo:123',
customerId: 'C12345678',
encryptedMnemonic: 'new-encrypted-mnemonic',
isPrimary: false,
});
// Delete account
const deletedAccount = await db.ixoAccounts.v1.deleteIxoAccount({
address: 'ixo1abc123',
});Matrix Accounts
Manage Matrix account data:
// Select accounts
const account = await db.matrixAccounts.v1.selectMatrixAccount({
address: 'matrix-address-123',
});
const accounts = await db.matrixAccounts.v1.selectMatrixAccounts({
addresses: ['matrix-address-123', 'matrix-address-456'],
});
const accountsByUsername = await db.matrixAccounts.v1.selectMatrixAccountsByUsername({
username: 'user123',
});
const accountsByCustomer = await db.matrixAccounts.v1.selectMatrixAccountsByCustomerId({
customerId: 'C12345678',
});
// Request Matrix OpenID token
const openIdToken = await db.matrixAccounts.v1.requestMatrixAccountOpenIdByCustomerId({
customerId: 'C12345678',
homeserverUrl: 'https://matrix.example.com',
});
// Returns: { access_token, token_type, matrix_server_name, expires_in }
// Insert account
const newAccount = await db.matrixAccounts.v1.insertMatrixAccount({
address: 'matrix-address-123',
username: 'user123',
encryptedPassword: 'encrypted-password',
tempEncryptedMnemonic: 'temp-encrypted-mnemonic',
encryptedPin: 'encrypted-pin',
encryptedAccessToken: 'encrypted-access-token',
roomId: '!room123:matrix.org',
});
// Update account
const updatedAccount = await db.matrixAccounts.v1.updateMatrixAccount({
address: 'matrix-address-123',
tempEncryptedMnemonic: 'new-temp-encrypted-mnemonic',
encryptedPin: 'new-encrypted-pin',
encryptedAccessToken: 'new-encrypted-access-token',
roomId: '!room456:matrix.org',
});
// Delete account
const deletedAccount = await db.matrixAccounts.v1.deleteMatrixAccount({
address: 'matrix-address-123',
});Config
Manage configuration data:
// Select config
const config = await db.config.v1.selectConfig({ id: 1 });
const configs = await db.config.v1.selectConfigs({ ids: [1, 2] });
const configByKey = await db.config.v1.selectConfigByKey({ key: 'app_name' });
const configsByKey = await db.config.v1.selectConfigsByKey({
key: 'feature_flag',
active: true,
});
// Insert config
const newConfig = await db.config.v1.insertConfig({
key: 'app_name',
value: 'Supamoto Bot',
active: true,
});Claim Issues
Manage claim issue data:
// Select claim issues
const claimIssue = await db.claimIssues.v1.selectClaimIssue({ id: 1 });
const claimIssues = await db.claimIssues.v1.selectClaimIssues({ ids: [1, 2] });
const issuesByClaim = await db.claimIssues.v1.selectClaimIssuesByClaimId({
claimId: 'claim-123',
});
const issuesByCustomer = await db.claimIssues.v1.selectClaimIssuesByCustomerId({
customerId: 'C12345678',
});
const issuesByCollection = await db.claimIssues.v1.selectClaimIssuesByCollectionId({
collectionId: 'collection-123',
});
// Insert claim issue
const newClaimIssue = await db.claimIssues.v1.insertClaimIssue({
claimId: 'claim-123',
customerId: 'C12345678',
collectionId: 'collection-123',
reason: 'Invalid data provided',
});
// Update claim issue
const updatedClaimIssue = await db.claimIssues.v1.updateClaimIssue({
id: 1,
reason: 'Updated reason',
});
// Delete claim issue
const deletedClaimIssue = await db.claimIssues.v1.deleteClaimIssue({ id: 1 });Utilities
Validators
Validate various data formats:
import { utils } from '@ixo/supamoto-bot-sdk';
// Matrix validators
const isValidUserId = utils.validators.isValidUserId('@user:matrix.org');
const isValidRoomId = utils.validators.isValidRoomId('!room123:matrix.org');
const isValidRoomAlias = utils.validators.isValidRoomAlias('#room:matrix.org');
const isValidMxcLink = utils.validators.isValidMxcLink('mxc://matrix.org/abc123');
const isValidEventId = utils.validators.isValidEventId('$event123');
const isValidEventType = utils.validators.isValidEventType('m.room.message');
// IXO validators
const isValidDid = utils.validators.isValidDid('did:ixo:123');
const isValidAddress = utils.validators.isValidAddress('ixo1abc123');
const isValidCollectionId = utils.validators.isValidCollectionId('123');
// Bid validators
const isValidBid = utils.validators.isValidBid('{"data": "bid"}');
const isValidBidRole = utils.validators.isValidBidRole('SA'); // PO, EA, SA, IAEncryption
Handle encryption operations:
import { utils } from '@ixo/supamoto-bot-sdk';
// Encryption utilities
const encrypted = utils.encryption.encrypt('sensitive data', 'password');
const decrypted = utils.encryption.decrypt(encrypted, 'password');Indexing
Handle search indexing:
import { utils } from '@ixo/supamoto-bot-sdk';
// Indexing utilities
const nameIndex = utils.indexing.createNameIndex('John Doe');
const addressIndex = utils.indexing.createAddressIndex('123 Main St');Claims Utilities
Handle claim-related operations:
import { utils } from '@ixo/supamoto-bot-sdk';
// Extract customer data from different claim types
const customerData = utils.claims.extractCustomerData('lead', claimData);
// Supports: 'lead', 'screening', 'contract'
// Returns: { customerId, givenName, familyName, nationalId, ... }
// Decrypt customer data field
const decryptedField = utils.claims.decryptCustomerDataField(encryptedBuffer, 'C12345678', encryptionKey);
// Encrypt customer data field
const encryptedField = utils.claims.encryptCustomerDataField('plain text data', 'C12345678', encryptionKey);
// Decrypt full customer data object
const decryptedCustomer = utils.claims.decryptCustomerData(customerData, encryptionKey);Customers Utilities
Handle customer-related operations:
import { utils } from '@ixo/supamoto-bot-sdk';
// Generate unique customer ID in format C21009802
const customerId = utils.customers.generateUniqueCustomerId();
// Returns: 'C' + 8-character alphanumeric hash (e.g., 'C21009802')Type Definitions
The SDK provides comprehensive TypeScript types for all database entities:
import type {
// Core types
PoolConfig,
// Entity types
ICustomer,
ICustomers,
IPhone,
IPhones,
ICustomerPhone,
ICustomerPhones,
IClaim,
IClaims,
ICustomerCredential,
ICustomerCredentials,
ICustomerOnboarding,
ICustomerOnboardings,
ICustomerSubscription,
ICustomerSubscriptions,
ISubscription,
ISubscriptions,
IHouseholdEntity,
IHouseholdEntities,
IHouseholdCredential,
IHouseholdCredentials,
IIxoAccount,
IIxoAccounts,
IMatrixAccount,
IMatrixAccounts,
IConfig,
IConfigs,
IClaimIssue,
IClaimIssues,
// Claim types
ILeadClaim,
ILeadClaims,
IScreeningClaim,
IScreeningClaims,
IContractClaim,
IContractClaims,
IOnboardingFeeClaim,
IOnboardingFeeClaims,
IHomeVisitClaim,
IHomeVisitClaims,
IFuelPurchaseClaim,
IFuelPurchaseClaims,
IBagStoveClaim,
IBagStoveClaims,
IThousandDayHouseholdClaim,
IThousandDayHouseholdClaims,
IExistingCustomerClaim,
IExistingCustomerClaims,
IUnresolvedPayment,
IUnresolvedPayments,
} from '@ixo/supamoto-bot-sdk';Error Handling
The SDK throws exceptions for database errors. Always wrap database operations in try-catch blocks:
try {
const customer = await db.customers.v1.selectCustomer({
customerId: 'C12345678',
});
console.log(customer);
} catch (error) {
console.error('Database error:', error);
// Handle the error appropriately
}Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
License
This project is licensed under the Apache 2.0 License - see the LICENSE file for details.
Support
For support, please open an issue on GitHub or contact the IXO team.
Note: This SDK is designed for use with the Supamoto Bot ecosystem and requires a PostgreSQL database with the appropriate schema.
