Getting started
You can read the contract and try mock registration now. The steps involving tokens and API calls below describe the proposed integration; they cannot run against a live Ginum service yet. Every .example hostname is illustrative.
1. Register an app
Section titled “1. Register an app”Open the app registration preview. Choose a fictional app name, a flow, named ACME companies and the smallest scope set. Review before registering. You can revoke the mock app afterward.
The planned live flow is invitation-only. A reviewed app owner registers metadata and exact redirects. A tenant administrator requests an installation for specific companies. Registration itself grants no data. Service read grants need an independent OWN approval; an installation containing draft scope also needs independent company FM approval. An eligible user must then consent personally.
2. Choose an authorization flow
Section titled “2. Choose an authorization flow”User apps: Authorization Code + PKCE
Section titled “User apps: Authorization Code + PKCE”- Create a random PKCE verifier and its S256 challenge with a maintained OAuth library. Keep the verifier for this one attempt.
- Send the browser to the issuer’s authorization endpoint with your registered client ID, exact redirect URI,
response_type=code, requested scopes, unpredictablestate,code_challengeandcode_challenge_method=S256. Include and validate an OIDC nonce if requesting OIDC. - On return, verify
stateand the exact callback. Exchange the one-use code and original verifier at the token endpoint. The proposed code lifetime is 60 seconds. - Use the access token with the API. An ID token is not an API credential. Public/native clients have no embedded client secret. Web BFFs hold tokens server-side behind protected sessions.
- Follow the issuer’s refresh rotation and revocation rules. Proposed access lifetime is five minutes; current authority is checked on every API request. Refresh cannot increase scope.
Discovery, real issuer URLs, client IDs and token handling must come from the future issuer release. This portal does not implement OAuth, collect credentials or issue a token. Never paste tokens into docs, URLs or browser storage.
Service apps: client credentials
Section titled “Service apps: client credentials”Use a separately reviewed service installation for exact companies and read scopes. Prefer asymmetric private_key_jwt client authentication through a maintained OAuth client. The token request uses grant_type=client_credentials and the approved scope subset. There is no refresh token. Keep private keys on your server in approved secret storage.
Services cannot create journal drafts, impersonate people or receive employee-own scopes. Do not substitute a shared human account. The read service grant has a proposed 90-day expiry and needs owner renewal.
3. Prepare a first call
Section titled “3. Prepare a first call”The following HTTP message is illustrative. The placeholder is not a credential, and the host is not deployed.
GET /api/v1/companies?limit=50 HTTP/1.1Host: api.sandbox.ginum.exampleAuthorization: Bearer <ACCESS_TOKEN_FROM_FUTURE_ISSUER>Accept: application/jsonA synthetic LK response looks like this:
{ "items": [ { "id": "10000000-0000-4000-8000-000000000001", "code": "ACME-LK", "legalName": "Synthetic Lanka Company", "country": "LK", "baseCurrency": "LKR", "timeZone": "Asia/Colombo" } ], "nextCursor": null, "asOf": "2026-11-06T08:00:00Z"}Use a returned company ID in the account definitions path. For periods, include from and to business dates, an inclusive range no longer than 366 days. That permission mapping still needs Ledger approval.
4. Plan a draft write
Section titled “4. Plan a draft write”Journal-draft creation needs a delegated eligible user, ledger.journal-drafts:write, an enabled Ledger module and a new random Idempotency-Key. Use 2–200 balanced lines, eligible ordinary accounts and the company base currency. Send UTF-8 JSON with no extra fields or duplicate keys.
A 201 response means draft created, never submitted, approved or posted. The returned Location identifies the draft; this slice has no GET-by-draft-ID or update operation. Do not invent one.

