Input, field, and event reference
This page is the field-level companion to Feature clients. ! in a GraphQL type means the server requires that field. A server may enforce additional conditional rules after GraphQL validation, as noted below. The backend schema is the source of truth; confirm these inputs against the version deployed at your endpoint.
SDK setup#
| Option | Type | Required | Description |
|---|---|---|---|
endpoint |
string |
Yes | Absolute GraphQL URL for the Malonlin API. |
uploadEndpoint |
string |
No | Same-origin multipart upload URL; defaults to the API origin's /upload route. |
accessToken |
string |
No | Initial user JWT. Prefer an in-memory token or getAccessToken callback in browser apps. |
getAccessToken |
() => string | null | Promise<string | null> |
No | Retrieve the current user token for requests. |
apiKey |
string |
No | API key for deployments that issue one. Never bundle a private key in a browser or mobile app. |
csrfToken |
string | () => string | null |
No | CSRF value when required by the deployment. |
headers |
Record<string, string> |
No | Extra headers; SDK-managed authorization and API-key headers take precedence. |
fetch |
typeof fetch |
No | Inject a compatible fetch implementation. |
timeoutMs |
number |
No | Positive request timeout in milliseconds; defaults to 30,000. |
deviceId |
string |
No | Stable per-install device identifier, sent as X-Device-Id. The server binds sessions and step-up checks to it. Use a value your app persists, not a fresh random value per launch. |
getStepUpCode |
(message: string) => string | null | Promise<string | null> |
No | Called when the server requires a fresh 2FA code for a sensitive operation. Return the user's 6-digit code, or null to cancel. See Step-up verification. |
Authentication and two-factor authentication#
Login#
| Field | GraphQL type | Required | Description |
|---|---|---|---|
email |
String! |
Yes | Account email. |
password |
String! |
Yes | Account password. |
twoFactorCode |
String |
No | Current authenticator code when two-factor authentication is enabled. |
const session = await sdk.auth.login({
email: 'you@example.com',
password: passwordFromSecureInput,
twoFactorCode, // omit when 2FA is not enabled
});2FA operations#
| SDK method | Arguments | Required / optional | Description |
|---|---|---|---|
generateTwoFactorSecret() |
None | None | Generates a setup secret for the authenticated account flow. |
beginTwoFactorSetup(adminId, password?) |
adminId: string, password?: string |
adminId required |
Starts setup and returns the server's setup value. The server requires password while 2FA is off, so pass the account password when enabling 2FA. |
confirmTwoFactorSetup(adminId, code) |
Both string |
Required | Verifies a code and enables setup. |
verifyTwoFactorCode(adminId, code) |
Both string |
Required | Verifies a code for the account. |
disableTwoFactor(adminId) |
adminId: string |
Required | Disables 2FA for the owning account. |
Keep setup secrets and authenticator recovery material private. Use the authenticated user's own ID for ID-based 2FA calls; the backend's ownership guard must reject attempts to operate on another account. Do not log setup responses, one-time codes, or login payloads.
Step-up verification#
Some sensitive operations require a fresh 2FA code even when the user is already signed in. These are the operations the server currently gates:
sdk.wallet.withdrawandsdk.wallet.transfer- The bank withdrawal (
manualWithdrawalCreate) - Admin wallet withdrawals
When the server requires a code, it returns an error whose message starts with STEP_UP_REQUIRED. The SDK then calls getStepUpCode(message), retries the request with the X-Step-Up-Code header, and makes up to three attempts in total.
import { MalonlinSDK, StepUpRequiredError } from 'malonlin-sdk';
const sdk = new MalonlinSDK({
endpoint,
getAccessToken,
deviceId: await getOrCreateInstallId(),
getStepUpCode: async (message) => askUserForAuthenticatorCode(message),
});
try {
await sdk.wallet.withdraw({ amount, currency: 'ZAR' });
} catch (error) {
if (error instanceof StepUpRequiredError && error.isNetworkChange) {
// The network changed. Ask the user to sign in again with a 2FA code.
}
}- If
getStepUpCodeis not set, or it returnsnull, the SDK throwsStepUpRequiredError. - When
isNetworkChangeistrue, the SDK does not prompt. The server has rejected the session because its network changed, so send the user through a fresh sign-in. - Retries keep the same idempotency key. The server rejects step-up before it writes anything to the ledger, so a retry is safe.
Cart and checkout#
Authenticated self-service inputs#
| Input | Field | GraphQL type | Required | Description |
|---|---|---|---|---|
SelfCheckoutInput |
cartId |
String! |
Yes | Cart to check or submit. The backend checks cart ownership. |
paymentMethod |
String! |
Yes | Payment method understood by the deployed checkout service. | |
couponCode |
String |
No | Coupon to validate and apply. | |
fulfillmentType |
FulfillmentType |
No | Fulfillment choice; required by some product/fulfillment combinations. | |
shippingAddress |
ShippingAddressInput |
No | Required by the backend when the selected fulfillment needs delivery. | |
ShippingAddressInput |
address |
String! |
Yes when supplied | Address line or formatted delivery address. |
city |
String |
No | City or locality. | |
contactName |
String |
No | Recipient name. | |
contactPhone |
String |
No | Recipient contact number. | |
country |
String |
No | Country. | |
postalCode |
String |
No | Postal code. | |
state |
String |
No | Province/state. |
sdk.getOrCreateMyCart() and sdk.getMyCart() resolve the signed-in user's cart without accepting a caller-supplied user ID. sdk.myCheckoutReadiness(input) is a preview only; the server recalculates totals and validates ownership at checkout. sdk.checkoutMyCart(input) uses the self-scoped mutation and sends an idempotency key. A successful checkout response is authoritative only when the server says the order is paid.
Product create and update fields#
CreateProductDto and UpdateProductDto accept the following fields. GraphQL requires only name and price when creating a product; the backend additionally enforces conditional rules by productType. On update, every listed field is optional.
| Field | GraphQL type | Create required? | Description |
|---|---|---|---|
name |
String! on create; String on update |
Yes on create | Product display name. |
price |
Float! on create; Float on update |
Yes on create | Base price. For SERVICE, backend normalization derives the price from duration and the selected rate. |
productType |
ProductType |
No; defaults to PHYSICAL |
One of PHYSICAL, PRE_ORDER, SERVICE, DIGITAL. |
description |
String |
No | Product details. |
handle |
String |
No | Storefront URL handle. |
images |
[String!] |
No; defaults to [] |
Image URLs/keys. |
tags |
[String!] |
No; defaults to [] |
Search and organization tags. |
categories |
[String!] |
No; defaults to [] |
Product categories. |
published |
Boolean |
No; defaults to false |
Whether the listing is published. |
featured |
Boolean |
No; defaults to false |
Whether the listing is featured. |
currencyCode |
String |
No | ISO currency code. ZAR is the primary payment currency. |
compareAtPrice |
Float |
No | Optional reference price. |
costPerItem |
Float |
No | Seller-side item cost. |
taxPercent |
Float |
No | Tax percentage from 0 to 100. |
discountType |
DiscountType |
No | Discount calculation mode. |
discountValue |
Float |
No | Discount amount/value. |
totalInventory |
Int |
No; defaults to 0 |
Physical stock or pre-order capacity. Service/digital inventory is normalized by the backend. |
variants |
[CreateVariantDto!] |
No; defaults to [] |
Product variants. |
shippingMode |
ProductShippingMode |
No | Tangible product pickup/delivery mode. |
shippingCostEntries |
[ShippingCostEntryInput!] |
No | Seller-defined shipping labels and amounts. |
hasRefundPolicy |
Boolean |
No; defaults to false |
Enable a custom refund policy. |
refundPolicyDescription |
String |
No | Human-readable refund terms. |
refundWindowDays |
Int |
No; defaults to 30 |
Eligibility window; backend documents a 0-365 day range. |
refundRequiresApproval |
Boolean |
No; defaults to true |
Require owner approval for refunds. |
estimatedPrepTime |
Int |
No in SDL; conditionally required for PRE_ORDER |
Estimated preparation time in minutes; must be at least 1. |
isAvailableForPreOrder |
Boolean |
No | Whether pre-ordering is currently available. |
allowPreOrderWaitlist |
Boolean |
No | Allow overflow into the pre-order waitlist. |
preOrderFulfillmentTypes |
[String!] |
No in SDL; conditionally required for PRE_ORDER |
Must include at least one supported type, such as pickup or delivery. |
maxPreOrderQuantityPerOrder |
Int |
No | Maximum quantity per order; normalized to at least 1 for pre-orders. |
trackPreOrderCapacityByVariant |
Boolean |
No | Track pre-order capacity at variant as well as product level. |
requiresBooking |
Boolean |
No | Booking flag; forced on for SERVICE. |
serviceDuration |
Int |
No in SDL; conditionally required for SERVICE |
Estimated billable duration in minutes; must be at least 1. |
serviceLocation |
String |
No | Where the service takes place. |
availableSlots |
[ServiceAvailabilitySlotInput!] |
No in SDL; conditionally required for SERVICE |
At least one valid service slot is required for a service listing. |
maxBookingsPerSlot |
Int |
No | Booking capacity per slot. |
servicePricingModel |
ServicePricingModel |
No | Duration-based or fixed-fee pricing mode. |
serviceRates |
ServiceRateCardInput |
No in SDL; conditionally required for SERVICE |
Must include at least one positive hourly, daily, weekly, or monthly rate. |
defaultServiceRateUnit |
String |
No | Active rate unit; backend chooses a valid available rate when omitted/invalid. |
serviceOperationalFlatRate |
Float |
No | One-time operating cost added to a service estimate. |
digitalFiles |
[DigitalFileInput!] |
No in SDL; conditionally required for DIGITAL |
At least one downloadable file; current backend guide limits files to PDF. |
Nested input details:
| Input | Field | Type | Required | Description |
|---|---|---|---|---|
ServiceAvailabilitySlotInput |
startTime |
String! |
Yes | Start time in HH:mm. |
endTime |
String! |
Yes | End time in HH:mm; must be later than the start. |
|
dayOfWeek |
Int |
Conditional | Recurring day, 0 (Sunday) through 6 (Saturday). | |
date |
String |
Conditional | One-off date in YYYY-MM-DD. Supply date or dayOfWeek. |
|
ServiceRateCardInput |
hourly, daily, weekly, monthly |
Float each |
At least one positive rate for SERVICE | Billing-unit rates. |
DigitalFileInput |
fileName, s3Key, mimeType |
String! each |
Yes | Stored file metadata; use a server-issued key, not a client-supplied arbitrary object path. |
fileSizeBytes |
Int! |
Yes | File size in bytes. |
See Product Types and Capabilities for type normalization and fulfillment behavior.
Wallet, payment, and subscription inputs#
Wallet transfer and withdrawal#
| Input | Field | Type | Required | Description |
|---|---|---|---|---|
TransferInput |
toUserId |
String! |
Yes | Recipient account identifier. |
amount |
Float! |
Yes | Positive amount; server validates balance and limits. | |
currency |
String! |
Yes | Source currency. | |
toCurrency |
String |
No | Optional recipient currency. | |
description |
String |
No | Transfer memo. | |
idempotencyKey |
String |
No in SDL | SDK generates and reuses a stable key for the same retry. | |
WithdrawInput |
amount |
Float! |
Yes | Requested amount. |
currency |
String! |
Yes | Withdrawal currency. | |
description |
String |
No | Optional memo. | |
idempotencyKey |
String |
No in SDL | SDK supplies a key when one is omitted. | |
metadata |
String |
No | Optional serialized metadata. |
The sdk.wallet.myWallet() and sdk.wallet.myTransactions() methods are self-scoped and do not accept a user ID. Prefer them over ID-based wallet queries in user-facing applications.
withdraw, transfer, and bank withdrawals (manualWithdrawalCreate) need a step-up 2FA code. Configure getStepUpCode before calling them; see Step-up verification.
Secure payment verification request#
| Field | Type | Required | Description |
|---|---|---|---|
userId |
String! |
Yes | Must match the authenticated user. |
amount |
Float! |
Yes | Positive payment amount. |
type |
String! |
Yes | Payment operation type understood by the backend. |
idempotencyKey |
String! |
Yes at GraphQL boundary | SDK generates/reuses a secure key if omitted from the client method input. |
currency |
String! |
No; defaults to ZAR |
Payment currency. |
description |
String |
No | Optional payment description. |
recipientId |
String |
No | Recipient for transfer-like operations. |
This creates a multi-party verification request; it is not a card gateway or proof that the payment has completed. Poll paymentVerificationStatus through an authenticated backend flow and act only on a terminal server status. The primary and website API deployments enforce that the request/status belongs to the authenticated user.
Ozow wallet deposit modal#
sdk.wallet.startOzowDepositModal(userId, input) calls the authenticated ozowPayInModalStart operation. The required userId must be the signed-in user's ID. Never calculate the Ozow hash or expose OZOW_PRIVATE_KEY in a client.
| Field | Type | Required | Description |
|---|---|---|---|
GraphQL userId argument |
String! |
Yes | Authenticated wallet owner; server ownership guard compares it with the JWT. |
input.amount |
Float! |
Yes | Positive ZAR top-up amount. |
customer |
String |
No | Customer display name. |
customerCellphoneNumber |
String |
No | Verified digits-only phone, excluded from the hash. |
adminId, accountNumber |
String |
No | Optional reconciliation identifiers. |
optional1-optional5 |
String |
No | Optional merchant metadata. |
successUrl, cancelUrl, errorUrl, notifyUrl |
String |
No | Callback overrides; production callbacks should normally be configured server-side. |
isTest |
Boolean |
No | Honored only by non-production server configuration. |
The API returns paymentUrl, server-signed postData, transactionReference, and bankReference. Use Ozow's official browser integration (createPaymentModal or createPaymentFrame) to render its hosted payment UX; Malonlin's SDK itself is framework-agnostic and does not inject an iframe or control Ozow's payment page.
const started = await sdk.wallet.startOzowDepositModal(userId, { amount: 250 });
// Load the official Ozow integration script in response to the user's action,
// then hand the server-generated values directly to that provider SDK.
const ozow = new window.Ozow();
ozow.createPaymentModal(started.paymentUrl, started.postData);
// Treat the browser callback as a UX signal only. Reconcile by reference with
// the backend before showing funds as available.
const verification = await sdk.wallet.ozowTransaction(started.transactionReference);Do not build a custom frame for the payment form, rewrite provider parameters, trust a browser redirect as payment confirmation, or show wallet funds until the Ozow callback/reconciliation confirms completion. A payment frame is cross-origin/provider-controlled; CSP and script-loading policy belong to the consuming application.
Subscription purchase#
| Input | Field | Type | Required | Description |
|---|---|---|---|---|
PurchaseSubscriptionInput |
subjectId |
String! |
Yes | User or store receiving the subscription. |
subjectType |
SubscriptionSubjectType! |
Yes | USER or STORE. |
|
planCode |
String |
No in SDL | Plan code; provide planCode or planId as required by the deployed resolver. |
|
planId |
String |
No in SDL | Plan ID; provide planId or planCode. |
|
paymentRef |
String |
No | External payment reference where supported. | |
startDate, endDate |
DateTime |
No | Optional schedule dates. | |
PurchaseSubscriptionAddonInput |
subjectId |
String! |
Yes | User or store receiving the add-on. |
subjectType |
SubscriptionSubjectType! |
Yes | USER or STORE. |
|
addonCode |
String! |
Yes | Add-on identifier. | |
quantity |
Int! |
Yes | Number of add-ons. | |
paymentRef |
String |
No | Payment reference where supported. |
Use sdk.subscriptions.plans(), addons(), and the relevant self/store subscription context before purchase. The server checks eligibility and entitlements; a client-side plan display is not authorization.
Customer profile and address inputs#
| Input | Field | Type | Required | Description |
|---|---|---|---|---|
CreateCustomerInput |
email |
String! |
Yes | Customer email. |
firstName, lastName, phone, companyName |
String |
No | Customer identity/contact data. | |
defaultBillingAddressId, defaultShippingAddressId |
String |
No | Preferred address references. | |
metadata |
[MetadataInput!] |
No | Optional key/value metadata. | |
CreateAddressInput |
addressName, address1, city, countryCode, postalCode |
String! |
Yes | Address label and required postal location. |
isDefaultBilling, isDefaultShipping |
Boolean! |
Yes | Whether this address is the default for either purpose. | |
address2, company, firstName, lastName, phone, province |
String |
No | Additional contact and location details. | |
metadata |
[MetadataInput!] |
No | Optional key/value metadata. |
Prefer myCustomer, myCustomerAddresses, createMyCustomer, and createMyCustomerAddress for the authenticated profile. Prefer myBillingAddresses/myShippingAddresses and their matching createMy*, updateMy*, and removeMy* methods for checkout addresses; these avoid carrying another user's raw ID through the application.
Ratings#
Create and update#
CreateRatingInput fields:
| Field | Type | Required | Description |
|---|---|---|---|
rating |
Int! |
Yes | Rating score. The server applies the allowed score range. |
targetType |
RatingTargetType |
No in GraphQL | STORE, PRODUCT, or SERVICE; backend resolves the target/source eligibility. |
sourceType |
RatingSourceType |
No in GraphQL | ORDER, DIGITAL_PURCHASE, or SERVICE_BOOKING. |
sourceId, storeId, productId, ownerType, ownerId, userId |
String |
No in GraphQL | Resource/actor references; authenticated identity and completed fulfillment are validated server-side. |
title, comment |
String |
No | Review text. |
pros, cons, mediaUrls |
[String!] |
No | Optional review details/media references. |
wouldRecommend |
Boolean |
No | Recommendation indicator. |
UpdateRatingInput has optional rating, title, comment, pros, cons, mediaUrls, and wouldRecommend fields. A review is not accepted merely because these input fields are present: the server requires a completed eligible fulfillment and enforces one review per source. VoteRatingInput requires ratingId: String!, userId: String!, and isHelpful: Boolean!; the user must be the authenticated voter, and voting is single-use.
Webhook subscriber fields and event schemas#
Subscriber management#
| Operation | Field | Type | Required | Description |
|---|---|---|---|---|
| Create | storeId |
String |
Yes | Store whose paid orders should produce events. |
name |
String |
Yes | Subscriber label, 1-120 characters. | |
endpointUrl |
String |
Yes | Public HTTPS receiver URL; backend also blocks unsafe/private resolution. | |
eventTypes |
[String!] |
Yes | Non-empty list; currently only payment.completed is supported. |
|
| Update | id |
String |
Yes | Subscription identifier. |
name, endpointUrl, eventTypes, isActive |
String, String, [String!], Boolean |
No | Only supplied values are updated. | |
| Delete | id |
String! |
Yes | Removes the subscription. |
| Deliveries | limit |
Int |
No | Optional history limit (1-100 in the SDK). |
The one-time webhook secret is returned only when the subscription is created. Store it server-side; never send it to a browser or mobile client.
Outbound payment.completed webhook#
| Location | Field | Type | Description |
|---|---|---|---|
| Header | X-Malonlin-Event-Id |
string | Stable delivery/event ID for deduplication. |
| Header | X-Malonlin-Timestamp |
Unix seconds | Timestamp included in the signature input. |
| Header | X-Malonlin-Signature |
v1=<hex> |
HMAC-SHA256 signature of ${timestamp}.${exactRawBody}. |
| Envelope | id |
string | Same event identifier as the header. |
| Envelope | createdAt |
ISO timestamp | Event creation time. |
| Envelope | type |
string | Currently payment.completed. |
data |
orderId, orderNumber |
string | Paid order identifiers. |
data |
amount |
number | Paid amount. |
data |
currency |
string | Payment currency, typically ZAR. |
data |
paidAt |
ISO timestamp | Server-confirmed payment time. |
The backend delivers only after isPaid: true, retries non-2xx/network failures up to eight times, and does not wait for the subscriber during checkout. See Events and webhooks for signature verification, raw-body handling, and idempotent receiver code.
Local SDK event map#
These are in-process events from sdk.on; they are not backend webhooks or GraphQL subscriptions.
| Event | Payload fields and types | Description |
|---|---|---|
cart:synced |
Cart (id: string, items: CartItem[], plus returned cart fields) |
Cart loaded or synchronized after a mutation. |
cart:updated |
Same Cart payload |
Successful SDK cart change. |
checkout:started |
cartId: string, idempotencyKey: string |
Checkout request begins. |
checkout:completed |
cartId: string, idempotencyKey: string |
Backend confirms checkout response. |
checkout:failed |
Checkout fields plus error: unknown |
Checkout request failed; original error is preserved. |
request:started |
operation: string |
GraphQL request or upload starts. |
request:completed |
operation: string, durationMs: number |
Request succeeds. |
request:failed |
operation: string, durationMs: number, error: unknown |
Request fails; original error is preserved. |
Event handlers run in the SDK process only. Avoid logging auth headers, personal payload data, secrets, or payment details from listeners.
Feature boundary cross-check#
This SDK covers the Malonlin API domains and feature flows that are represented by the deployed GraphQL schema and the website API surface. It does not turn every backend administrative or internal operation into a client feature; staff-only configuration, reconciliation, and maintenance actions remain behind their server authorization boundary.
The current checked-in malonlin-n and website frontend contain no mini-app API or mini-app route contract to expose. Mini-app support is therefore not currently an SDK feature; it should be added only after a backend schema and authorization flow exist. Likewise, browser framework hooks and presentation-only settings are app-specific, not framework-neutral SDK APIs.