Conventions and wrappers

Every operation and model is in Swagger; this page covers how the SDK wraps them. Use sdk.api to browse services such as companies, documents, and conversations in your IDE.

Calling conventions

Kotlin calls are suspend functions and return a response wrapper: .body() decodes it, and the wrapper also carries status and headers. TypeScript methods take one parameter object and return the decoded body; reach status and headers through sdk.raw.

Path and query parameters use their Swagger names. For generated operations, the request body goes in input on sdk.api calls and in cmd on mutation hooks and *MutationOptions; Kotlin names it after its type, for example updateSectionCommand. The upload helpers take request instead (see Documents).

TypeScript results are frozen, so items.sort() or map.set() throws. Copy first, with items.toSorted(), [...items], { ...company } or new Map(map).

Finding the wrapper for an operation

Query options, mutation options and cache keys come from @solibo/solibo-query, and hooks from @solibo/solibo-react (package list). They are named after resources, not operations, and some rename parameters: sdk.api.companies.showCompany({ companyId }) becomes useCompanyById({ id }) and companyByIdQueryOptions(sdk, { id }).

For an endpoint without a named hook, openApiOperationQueryOptions(sdk, { operationId, args, execute }) wraps any call. execute receives the raw SDK (positional bigint arguments, response wrapper), and operationId only labels the cache key. openApiOperationMutationOptions and the React hooks useOpenApiOperationQuery / useOpenApiOperationMutation work the same way.

import { openApiOperationQueryOptions } from '@solibo/solibo-query'

const company = await queryClient.fetchQuery(
  openApiOperationQueryOptions(sdk, {
    operationId: 'showCompany',
    args: [BigInt(companyId)] as const,
    execute: (raw, [id]) => raw.api.companies.showCompany(id),
  }),
)

useIssues, useMeetings and useOwnershipChanges read across every housing community (API: company) you can access when companyId is omitted. While the id is loading, pass enabled: !!companyId to useIssues, or spread the matching query options with it into your own useInfiniteQuery call.

Cache keys and defaults

Query keys start with a key-factory array: [issueKeys.list(companyId), ...filters]. Invalidate with { queryKey: [issueKeys.list(companyId)] }, outer brackets included; [issueKeys.all] refreshes every issue query.

Query and React wrappers stay disabled until their required ids are set, include fields in the query key, and invalidate related queries after a successful mutation. Some set a staleTime (up to an hour); the rest use TanStack Query’s default of 0. To change a default, spread the query options into your own useQuery call and override it.

Errors

Client A failed call
Kotlin Throws SoliboSDKError (statusCode; errors, each with property and message; kind; reason). A 429 that outlasts the retries throws RateLimitException, and a sign-in endpoint that answers with a redirect throws RedirectException. A 401 is not thrown: check response.success before calling .body().
TypeScript Facade calls throw SoliboApiError with status and errors. The underlying Kotlin error, with kind and reason, is on .cause, and status is undefined for rate limits and network errors.
React Mutation hooks also offer onValidationError(errors => …) on the mutation result, which receives the field errors of a 422.

The client has already retried 429 responses, and 5xx responses to reads (see What the client does for you): show the message and let the person try again. A failed mutation was sent only once and may still have been applied, so refresh what the person sees before they repeat it. Authentication methods may also return null or false; see Authentication and MFA. Wire format, including the two error shapes: developers.solibo.no/api#errors.

Paged lists

Paged endpoints return items, meta, and paging. Pass paging.next as pageToken for the next request, and stop when paging.next is null, absent or END_OF_LIST. Some endpoints return arrays or other collection shapes instead.

Walking pages

import no.solibo.oss.sdk.api.gen.models.Company

val companies = mutableListOf<Company>()
var pageToken: String? = null
do {
    val page = sdk.api.companies.indexCompany(pageToken = pageToken, count = 100).body()
    companies += page.items
    pageToken = page.paging.next?.takeUnless { it == "END_OF_LIST" }
} while (pageToken != null)
import type { Company } from '@solibo/solibo-sdk'

const companies: Company[] = []
let pageToken: string | undefined
do {
  const page = await sdk.api.companies.indexCompany({ pageToken, count: 100 })
  companies.push(...page.items)
  pageToken = page.paging.next && page.paging.next !== 'END_OF_LIST' ? page.paging.next : undefined
} while (pageToken)

In React, paged hooks expose data.pages, fetchNextPage, and hasNextPage:

import { useIssues } from '@solibo/solibo-react'

function IssueList({ companyId }: { companyId: number }) {
  const { data, isPending, isError, fetchNextPage, hasNextPage, isFetchingNextPage } = useIssues({ companyId })
  const issues = data?.pages.flatMap(page => page.items ?? []) ?? []

  if (isPending) return <p>Loading…</p>
  if (isError) return <p>Could not load issues.</p>

  return (
    <>
      {issues.map(issue => <div key={String(issue.id)}>{issue.title}</div>)}
      {hasNextPage && (
        <button disabled={isFetchingNextPage} onClick={() => fetchNextPage()}>Load more</button>
      )}
    </>
  )
}

autoPaginate: true asks the server for every page in one response. Use it for short lists, and walk the pages when a list can be long.

Field projections

Pass fields to request fewer response fields. Omitted fields may be absent even when the full model requires them. See Field projections for safe Kotlin and TypeScript examples.

Documents

Start with upload examples or reusing documents.

Task React hook
Upload one private file useUploadDocument()
Upload to a resource useUploadDocumentBelongsTo()
Upload several private files useMultiUploadDocument()
Upload to a public conversation usePublicUploadDocumentToConversation()
Upload several public conversation files useMultiUploadPublicDocumentToConversation()
Create a directory useCreateDocumentDirectory()
Reuse an existing file useCreateDocumentReference()
Resolve a private download URL useGetDocumentURL(params)
Resolve a public download URL usePublicDocumentURL(params)
Resolve a public conversation document URL usePublicConversationDocumentURL(params)
Resolve a public URL from an event handler usePublicGetDocumentURL(params)
Sign up to 100 private documents at once useDocumentURLs(params)
Sign up to 100 public documents at once usePublicDocumentURLs(params)

Query factories use the same names without use, followed by MutationOptions or QueryOptions (for example, uploadDocumentMutationOptions). The exception is usePublicDocumentURL, whose factory is publicGetDocumentURLQueryOptions.

The URL hooks cache each rendition, so components that show the same picture share one request. They stay idle until every id is set. The private and public ones also send the lookups a screen makes together as one batch request per company and rendition; a lookup made alone, or with fields, keeps the single-document endpoint. usePublicConversationDocumentURL always asks for the URL. Its sibling usePublicConversationDocument passes redirect through, and that endpoint redirects unless redirect is false.

usePublicGetDocumentURL is imperative. Each call is a request that bypasses the cache, so use it for downloads and other clicks, not for rendering. Configure it with companyIdOverride and call getDocumentURL(documentId), or configure it with slug and call getDocumentURL(conversationId, documentId). The slug (companySlug in other helpers) is the housing community’s public homepage name (Company.slug). A public conversation is an inquiry sent from that page by someone who is not signed in; companyIdOverride is the companyId.

Choosing a multi-file helper

Private uploads take companyId, documentType, and fileList. Add parentId for a directory or belongsToId for resource attachments. Public conversation uploads take companySlug, conversationId, and fileList.

Both multi-file helpers accept FileList or File[] and return a settled result for each file in input order. Inspect each result’s status, even when the mutation succeeds; one failed file does not stop the others. An optional onUploadProgress receives { file, loaded, total, fraction }.

Integrated resource mutations

To create or update a resource together with its documents, use sdk.api.integratedDocuments in Kotlin, *WithDocumentsMutationOptions in TanStack Query, or use*WithDocuments() in React. These helpers cover posts, comments, newsletters, practical information, neighborhood groups, conversations and their draft initial messages, and conversation messages.

Use DocumentInput.Upload(...) / DocumentInput.Existing(...) in Kotlin. TypeScript accepts { file }, { fileName, bytes, contentType }, or { sourceDocumentId }. Depending on the resource, pass them as image, attachments, or named inlineDocuments slots. See the post example.

The resource change and document references are saved together after uploads are prepared. Query and React helpers refresh related caches on success.

For conversations, use createConversation(...) / useCreateConversationWithDocuments() for the initial message and updateConversation(...) / useUpdateConversationWithDocuments() to edit a draft’s initial message. Both accept attachments, inlineDocuments, and idempotencyKey; updates also accept removeDocumentIds and require command.content. The update operation only accepts draft conversations. Create keeps its { conversation, documents } result; update returns { resource, documents }, where documents contains the finalized content, document list, and idempotency key. The Kotlin create helper keeps onUploadProgress as its fourth argument; use named arguments for the appended inlineDocuments and idempotencyKey.

Failure and retry behavior
  • Conversation creation and draft updates use the same atomic finalization and retry behavior as message creation. Upload failures do not create a conversation. The deprecated CreateConversationDocumentUploadException is no longer thrown.
  • For integrated mutations, a definitive backend rejection (a 4xx other than 408, 425 or 429) triggers upload cleanup and is not retried. Cleanup failures appear in IntegratedDocumentMutationException.rollbackFailures; the exception’s statusCode is the refusal’s status, and its cause is the SoliboSDKError with the backend’s error details.
  • Automatic finalization retries reuse the prepared document inputs and idempotency key.
  • If the final response is lost, the outcome may be unknown and staged uploads are retained. Use the exception’s idempotencyKey to identify and reconcile the mutation before invoking the helper with files again: a new helper call prepares new upload tickets, so reusing the key with those new tickets can conflict with the original request. A call using only existing-document references can be retried with the same key and unchanged request data.
  • IntegratedDocumentMutationCancellationException exposes phase, outcomeUnknown, and idempotencyKey. Cancellation during finalization can still leave a completed resource; cancellation during upload has outcomeUnknown = false.

Download and delete behavior

Download helpers return a signed URL. Request a fresh URL to change image transformations; see image options.

deleteDocument(...) deletes document metadata. It does not follow a storage delete URL returned in the response’s Location header. Only send DELETE to that URL when you intend to permanently remove the underlying file too.

Accounting queries

Query factory React hook
accountingOverviewQueryOptions useAccountingOverview
accountingPortfolioOverviewQueryOptions useAccountingPortfolioOverview
accountingProjectQueryOptions useAccountingProject
accountingEntriesQueryOptions useAccountingEntries
companyAccountingEntriesQueryOptions useCompanyAccountingEntries

Use the parameters offered by sdk.api.accounting for company filters, dimensions, search, pagination, and field selection. Portfolio and entry hooks return infinite pages.

Periods include periodStart and exclude periodEnd. Keep monetary amounts as decimal strings. Read company coverage and availability metadata alongside results; missing availability does not mean data is available. Your portfolio is the set of housing communities you can access. Leave companyIds out to use all of them; the accounting endpoints reject an explicitly empty list.

Bank account queries

Query factory React hook
companyBankAccountsQueryOptions useCompanyBankAccounts
companyBankAccountTransactionsQueryOptions useCompanyBankAccountTransactions

Both take the parameters of the matching sdk.api.bankAccounts operation, including fields, pageToken, autoPaginate and count, and return infinite pages. Transactions are newest first, with undated transactions last. Pass from and to together as YYYY-MM-DD dates, or omit both to include undated transactions. count accepts 1–100, and the bank provider does not support autoPaginate. Amounts are unsigned decimal strings; creditDebitIndicator gives the direction.

Meeting summons PDF

meetingSummonsPdfQueryOptions / useMeetingSummonsPdf({ companyId, meetingId }) fetch the summons PDF, which the server generates on request. It is only available while the meeting is PLANNING or NOTIFIED. The query is keyed under meetingKeys.detail(meetingId), so any mutation that refreshes the meeting also refreshes the PDF.

Section status

sectionStatusQueryOptions / useSectionStatus({ companyId, sectionId }) return the section’s statuses keyed by section id and then by status state, as plain objects (SectionStatusesBySection). Read one state as data[sectionId]?.CHECKED_IN. The raw api.sections.showSectionStatus returns the same nesting as a Map of Maps. sectionStatusesQueryOptions / useSectionStatuses({ companyId }) return one entry per section in the company, each with a statusMap keyed by status state.

Conversation state mutations

Use completeMultipleConversations or reOpenMultipleConversations with BatchConversationStateCommand.conversationIds. Matching *MutationOptions factories and useCompleteMultipleConversations() / useReOpenMultipleConversations() hooks are available.

Mark conversations as spam with spamConversation or spamMultipleConversations. Removing the mark with unspamConversation or unspamMultipleConversations sets the conversation back to NEW. Conversation lists and counts exclude SPAM unless the status filter names it, and ConversationCount.spam is 0 unless it does. The matching hooks are useSpamConversation(), useUnspamConversation(), useSpamMultipleConversations() and useUnspamMultipleConversations(). They refresh every conversation list and count after a change.

Push notifications

Task Factory React hook
List the device owner’s notifications pushNotificationInboxQueryOptions usePushNotificationInbox
List the notification types and the payload fields each carries pushNotificationTypesQueryOptions usePushNotificationTypes
Mark a notification read once its content is visible createPushNotificationReadMutationOptions useCreatePushNotificationRead
Report a notification received or opened reportPushNotificationReceivedMutationOptions, reportPushNotificationReadMutationOptions useReportPushNotificationReceived, useReportPushNotificationRead

Both queries take companyId, fields, pageToken, autoPaginate and count, and return infinite pages. An inbox item’s payload is typed by the notification’s type: NewOppslagPushPayload, NewPracticalInfoPushPayload, NewMessageInConversationPushPayload, NewNeighborhoodPostPushPayload or NewCommunicationCommentPushPayload. Check it with instanceof in TypeScript or is in Kotlin. It is null for a notification whose data matches no type; the raw data map is still there.


Solibo AS