API Conventions
The durable contract for the Galanthus C ABI: versioning, status and outcome handling, result-envelope ownership, errors, interrupts, stores, secrets, and threading.
Header and Version
#include <galanthus/c_api/gln_capi.h>
The header is C-callable. C++ translation units get extern "C" declarations
from the header itself.
GLN_API_VERSION is the compile-time ABI constant in gln_capi.h. The current
value is 9u.
GLN_API uint32_t GLN_CALL gln_get_api_version(void);
GLN_API const char* GLN_CALL gln_get_library_version(void);
Bindings should check the loaded library before using the rest of the ABI:
if (gln_get_api_version() != GLN_API_VERSION) {
return 1;
}
Compile against the installed header that ships with the shared library being loaded. Do not infer struct layout or function signatures from generated HTML, older docs, or a copied binding file.
Calling Convention and Names
GLN_CALL is the ABI calling convention (__cdecl on Windows, platform default
elsewhere). GLN_API supplies import/export decoration. Hand-written FFI
prototypes must include both effects.
Current operation entry points are backend-oriented:
GLN_API gln_status_t GLN_CALL gln_open_fints_backend(
const gln_fints_config_t* in_config,
gln_state_store_t* in_state_store,
gln_continuation_store_t* in_continuation_store_or_null,
gln_secret_t* in_pin,
gln_backend_t** out_backend,
gln_error_t* out_error);
GLN_API gln_status_t GLN_CALL gln_retrieve_accounts(
gln_backend_t* in_backend,
gln_backend_result_t** out_result);
Input parameters use in_; output parameters use out_. Optional parameters
carry _or_null in their name.
Status Codes
Every fallible function returns gln_status_t. GLN_OK is zero.
typedef enum {
GLN_OK = 0,
GLN_ERR_INVALID_ARG = 1,
GLN_ERR_OUT_OF_MEMORY = 2,
GLN_ERR_TRANSPORT = 3,
GLN_ERR_PROTOCOL = 4,
GLN_ERR_REJECTED = 5,
GLN_ERR_TAN_REQUIRED = 6,
GLN_ERR_DECOUPLED_PENDING = 7,
GLN_ERR_VOP_CONFIRMATION_REQUIRED = 8,
GLN_ERR_NOT_FOUND = 9,
GLN_ERR_NOT_SUPPORTED = 10,
GLN_ERR_INTERNAL = 11,
GLN_ERR_FOLLOW_UP_REQUIRED = 12,
GLN_ERR_PLUGIN_NOT_FOUND = 13,
GLN_ERR_PLUGIN_NOT_TRUSTED = 14,
GLN_ERR_TOKEN_PERSIST_FAILED = 15,
GLN_ERR_RESUME_ARTIFACT_INVALID = 16
} gln_status_t;
| Code | Meaning |
|---|---|
GLN_OK | Call or operation succeeded. |
GLN_ERR_INVALID_ARG | Caller violated the function contract. |
GLN_ERR_OUT_OF_MEMORY | Allocation failed inside the library. |
GLN_ERR_TRANSPORT | HTTP, TLS, socket, or network failure. |
GLN_ERR_PROTOCOL | Provider protocol exchange failed. |
GLN_ERR_REJECTED | Provider rejected the requested action. |
GLN_ERR_TAN_REQUIRED | Operation paused for TAN input. |
GLN_ERR_DECOUPLED_PENDING | Operation paused for out-of-band approval polling. |
GLN_ERR_VOP_CONFIRMATION_REQUIRED | Operation paused for Verification of Payee confirmation. |
GLN_ERR_NOT_FOUND | Requested persisted artifact or key material is absent. |
GLN_ERR_NOT_SUPPORTED | Operation or helper is unavailable for this backend, build, or platform. |
GLN_ERR_INTERNAL | Library invariant failed. Treat this as a product bug. |
GLN_ERR_FOLLOW_UP_REQUIRED | Provider returned a manual follow-up state. |
GLN_ERR_PLUGIN_NOT_FOUND | A plugin-backed backend could not resolve its plugin. |
GLN_ERR_PLUGIN_NOT_TRUSTED | A plugin-backed backend found an untrusted plugin. |
GLN_ERR_TOKEN_PERSIST_FAILED | OAuth or token material could not be persisted. |
GLN_ERR_RESUME_ARTIFACT_INVALID | A persisted resume artifact is malformed, stale, or incompatible. |
Backend Result Envelopes
Backend operations return two layers of status:
- The C function return value says whether an envelope was produced.
- The
gln_backend_result_tenvelope says what happened in the provider operation.
For backend operations, a GLN_OK function return means *out_result is
non-null and must be destroyed with gln_destroy_backend_result. Inspect the
envelope even when the operation itself failed or paused:
gln_backend_result_t* result = NULL;
gln_status_t call_status = gln_retrieve_accounts(backend, &result);
if (call_status != GLN_OK) {
/* No envelope exists. This is argument validation or allocation failure. */
return 1;
}
gln_status_t operation_status = gln_get_backend_result_status(result);
gln_backend_outcome_t outcome = gln_get_backend_result_outcome(result);
if (outcome == GLN_BACKEND_OUTCOME_SUCCESS && operation_status == GLN_OK) {
const gln_accounts_t* accounts = gln_get_backend_result_accounts(result);
for (size_t i = 0; i < gln_get_accounts_count(accounts); ++i) {
const gln_account_t* account = gln_get_account_at(accounts, i);
printf("%s\n", gln_get_account_iban(account));
}
}
else if (outcome == GLN_BACKEND_OUTCOME_ACTION_REQUIRED) {
const gln_interrupt_info_t* interrupt = gln_get_backend_result_interrupt_info(result);
gln_continuation_t* continuation = gln_take_backend_result_continuation(result);
/* Persist or resume continuation before destroying it. */
gln_destroy_continuation(continuation);
(void)interrupt;
}
else {
const gln_error_t* error = gln_get_backend_result_error(result);
fprintf(stderr, "%s: %s\n",
error && error->issue_type ? error->issue_type : "unknown",
error && error->message ? error->message : "");
}
gln_destroy_backend_result(result);
gln_get_backend_result_outcome classifies the operation:
| Outcome | Meaning |
|---|---|
GLN_BACKEND_OUTCOME_SUCCESS | The requested operation completed; use gln_get_backend_result_kind and the matching gln_get_backend_result_* accessor. |
GLN_BACKEND_OUTCOME_ACTION_REQUIRED | The operation paused; inspect gln_get_backend_result_interrupt_info and take the continuation if follow-up is possible. |
GLN_BACKEND_OUTCOME_REJECTED | The provider rejected the operation; inspect gln_get_backend_result_error. |
GLN_BACKEND_OUTCOME_ERROR | Validation, transport, protocol, plugin, token, or internal failure; inspect gln_get_backend_result_error. |
GLN_BACKEND_OUTCOME_UNKNOWN | Returned for a null result pointer or an uninitialized envelope. |
The envelope owns all borrowed views derived from it:
gln_get_backend_result_errorgln_get_backend_result_interrupt_infogln_get_backend_result_opaque_json- typed list and single-row handles returned by
gln_get_backend_result_* - row pointers returned by
gln_get_account_at,gln_get_balance_at,gln_get_transaction_at, and the other typed collection accessors - every
const char*returned by typed accessors
Those values are valid only until gln_destroy_backend_result. Do not release
them separately. Copy data into host-language objects before destroying the
envelope.
gln_take_backend_result_continuation is different: it transfers ownership of
the continuation out of the envelope. After taking it, destroy it with
gln_destroy_continuation or persist it with gln_save_continuation and then
destroy the in-memory handle.
Errors
Open functions, store constructors, continuation helpers, and similar
non-envelope functions use gln_error_t* output slots.
typedef struct {
uint32_t struct_size;
const char* issue_type;
const char* message;
const char* details;
gln_error_detail_t* detail;
} gln_error_t;
GLN_API gln_status_t GLN_CALL gln_default_error(gln_error_t* out_value);
Initialize each slot with gln_default_error before passing it to the library.
After a non-GLN_OK return, read the fields you need and call
gln_release_error. The string fields and detail are owned by the slot and
become invalid when it is released or reused.
For backend result envelopes, gln_get_backend_result_error returns a borrowed
const gln_error_t*. Do not pass that borrowed pointer to gln_release_error;
the envelope releases it.
Structured detail is optional. When present, inspect it before releasing the owning error slot or destroying the owning envelope:
const gln_error_detail_t* detail = gln_get_error_detail(error);
if (detail != NULL && gln_get_error_detail_kind(detail) == GLN_ERROR_DETAIL_EBICS_STATUS) {
const char* code = gln_get_error_detail_ebics_technical_code(detail);
(void)code;
}
Interrupts
Interrupt metadata is exposed through gln_interrupt_info_t:
typedef enum {
GLN_INTERRUPT_TAN_REQUIRED = 0,
GLN_INTERRUPT_DECOUPLED_PENDING = 1,
GLN_INTERRUPT_VOP_CONFIRMATION_REQUIRED = 2
} gln_interrupt_kind_t;
For backend result envelopes, use gln_get_backend_result_interrupt_info only when the
outcome is GLN_BACKEND_OUTCOME_ACTION_REQUIRED. The returned pointer is
borrowed from the envelope. Read the arm selected by kind, copy any fields
needed by the host language, then destroy the envelope after taking any
continuation.
Interrupt info is borrowed from gln_backend_result_t. Copy any fields needed
after the envelope is destroyed.
Ownership Summary
| Returned thing | Release with |
|---|---|
| Backend handle | gln_close_backend |
| Backend result envelope | gln_destroy_backend_result |
| Continuation taken from an envelope or loaded from a store | gln_destroy_continuation |
Populated direct gln_error_t slot | gln_release_error |
Library-owned char* output | gln_release_string |
| Library-owned binary buffer | gln_release_buffer |
| Secret handle | gln_destroy_secret |
| State store | gln_destroy_state_store |
| Continuation store | gln_destroy_continuation_store |
| EBICS key store | gln_destroy_key_store |
| Revolut token store | gln_destroy_revolut_token_store |
| Wise token store | gln_destroy_wise_token_store |
Destroy and release functions accept NULL unless the installed header says
otherwise. A handle becomes invalid as soon as its destroy or close function
returns.
Lifecycle and Threading
Call gln_init_runtime before opening backends, creating file-backed stores, or running
operations. Call gln_shutdown_runtime when the process is done with the library.
gln_error_t error = {0};
gln_default_error(&error);
gln_status_t s = gln_init_runtime(NULL, &error);
if (s != GLN_OK) {
gln_release_error(&error);
return 1;
}
/* Work with Galanthus. */
gln_shutdown_runtime();
Distinct handles may be used from distinct threads concurrently. Each handle, secret, continuation, and result envelope is single-threaded; do not share an individual handle between threads.
gln_init_runtime and gln_shutdown_runtime are not safe to interleave with
operations. Call gln_init_runtime once before the operating phase begins and
gln_shutdown_runtime once after it ends.
See Also
- Continuations - save, load, inspect, and resume model.
- Stores - state and continuation store vtables.
- Key store - EBICS key-store callback ownership.
- Secrets - wrapping PINs, TANs, and assertion keys.
- FFI examples - binding patterns and pitfalls.
- Troubleshooting - integration failure modes.