Skip to content

SDK Reference ​

Two SDK packages provide Sign It functionality. Both sign through the user's Flowsta Vault: the user approves each signature in a Vault dialog and the keys never leave their device. They differ in whether the signature reaches the network.

PackageUse CaseWhat You Get
@flowsta/auth (2.6.0)Web apps using OAuthDetects the user's local Vault and signs through it. The signature is returned to your app and is not published - nobody else can verify the file by it
@flowsta/holochain (3.6.0)Desktop Holochain appsDirect IPC call to Flowsta Vault. A linked app publishes the signature to the Sign It network from the person's device by default; anyone can then verify the file

@flowsta/auth - Web App Signing ​

For web apps that use "Sign in with Flowsta" (OAuth). Request the sign scope at login so the consent screen covers signing; the Vault path itself only needs a signed-in session.

hashFile(file) ​

Hash a file using SHA-256 in the browser via SubtleCrypto. The file is never uploaded.

typescript
import { hashFile } from '@flowsta/auth';

const file = document.querySelector('input[type=file]').files[0];
const hash = await hashFile(file);
// hash = "a7f3b9c1e2d4..." (64 hex characters)
ParameterTypeRequiredDescription
fileFileYesFile object from file input or drag-and-drop

Returns: Promise<string> - hex-encoded SHA-256 hash (64 characters)


signFile(options) ​

Request a signature for a file hash. The file is never uploaded - only the hash leaves your app.

signFile() probes for a running Flowsta Vault on every call:

  • Vault running (the normal case): signs via the Vault. The user approves in a Vault dialog that names your page by its origin and shows the first characters of the hash; the key never leaves their device. The Vault returns the signature to your app without publishing it - a web app cannot ask the Vault to publish, so action_hash is null, the file does not show as signed on the verify page, and no one else can verify it by this signature. People publish from their own Vault's Sign It page.
  • No Vault running: throws VaultRequiredError - signing keys live only in the user's Vault, so there's nothing else that can sign. Prompt the user to open or install Flowsta Vault. When the browser blocked the page from reaching the Vault, throws VaultBlockedError instead (2.5.0).
typescript
const result = await flowsta.signFile({
  fileHash: hash,
  intent: 'Authorship',
  aiGeneration: 'none',
  contentRights: {
    license: 'cc-by',
    aiTraining: 'not_allowed',
    contactPreference: 'allow_contact_requests',
  },
});
console.log('Approved by:', result.agent_pub_key, 'at', result.signed_at);
ParameterTypeRequiredDescription
fileHashstringYesSHA-256 hex string (64 characters)
intentstringNoAuthorship, Approval, Witness, Receipt, Agreement. Default: Authorship
aiGenerationstringNonone, assisted, generated
contentRightsRecord<string, string>NoSee Content Rights Object. camelCase keys are translated on the wire (2.6.0); snake_case keys are accepted as-is

Returns:

FieldTypeDescription
successbooleantrue on success
file_hashstringThe hash that was signed
agent_pub_keystringSigner's Holochain agent key (uhCAk... format)
signed_atnumber | stringISO 8601 string via the Vault; Unix milliseconds via the legacy API path
action_hashstring | nullnull via the Vault - the signature is not published. Set only by the legacy API path

Errors:

Error ClassCodeWhen
UserDeniedErroruser_deniedThe user declined the request in the Vault dialog
VaultRequiredErrorvault_requiredNo Vault is reachable - nothing else holds the user's signing keys. Also thrown (with a specific message) when the Vault is locked
VaultBlockedError (2.5.0)vault_blockedThe browser refused the loopback request (Chrome 142+ local network access permission, Brave's localhost block). The Vault may be running - point the user at the browser's site settings
VaultIdentityMismatchError (2.4.0)vault_identity_mismatchThe local Vault holds a different identity than the signed-in user - refused before any signature; tell the user to switch Vaults, never fall back to remote signing
FlowstaAuthErrortimeoutThe user didn't respond to the Vault dialog in time (~60 s)
FlowstaAuthErrorinvalid_file_hash, reserved_prefix, identity_mismatch, conductor_not_readyPassed through from the Vault: malformed hash, hash bytes beginning with flowsta-, the Vault refused under a different identity, the Vault is still starting

Requires: a signed-in session; request the sign scope at login


signBatch(options) ​

One approval per signature

The Vault approves one document at a time, so when a Vault is detected signBatch() throws FlowstaAuthError with code batch_requires_individual_approval. Call signFile() per file instead so the user can approve each signature. Without a Vault the call goes to the deprecated REST POST /sign-batch, which only works for legacy server-hosted identities.

typescript
const result = await flowsta.signBatch({
  files: [
    { fileHash: hash1 },
    { fileHash: hash2, intent: 'Approval' },
  ],
  sharedMetadata: {
    intent: 'Authorship',
    aiGeneration: 'none',
    contentRights: { license: 'all-rights-reserved' },
  },
});
console.log(`Signed ${result.signed}, failed ${result.failed}`);
ParameterTypeRequiredDescription
filesarrayYesArray of { fileHash, intent?, aiGeneration?, contentRights? }. Max 100 files
sharedMetadataobjectNoApplied to all files unless overridden per-file

Returns:

FieldTypeDescription
resultsarrayPer-file results: { file_hash, action_hash, success, error? }
signednumberCount of successfully signed files
failednumberCount of failed files

Requires: sign scope


verifyFile(fileHash) ​

Check if a file has been signed. Public endpoint - no authentication required. Verification is free on every plan (30 requests per minute per IP).

typescript
const result = await flowsta.verifyFile(hash);
if (result.count > 0) {
  result.signatures.forEach(sig => {
    console.log(`Signed by ${sig.signer} on ${new Date(sig.signed_at)}`);
    console.log(`License: ${sig.content_rights?.license}`);
  });
}
ParameterTypeRequiredDescription
fileHashstringYesSHA-256 hex string (64 characters)

Returns:

FieldTypeDescription
signaturesarrayArray of signature objects (see Verification API)
countnumberTotal number of signatures

getContentRights(fileHash) ​

Convenience wrapper over verifyFile() that returns just the content rights info from non-revoked signatures.

typescript
const rights = await flowsta.getContentRights(hash);
if (rights.signed) {
  rights.rights.forEach(r => {
    console.log(`${r.signer}: ${r.license}, AI training: ${r.aiTraining}`);
  });
}

Returns:

FieldTypeDescription
signedbooleanWhether the file has any signatures
signerCountnumberTotal number of signatures, revoked ones included
rightsarrayOne entry per non-revoked signature that declared rights: { signer, license?, aiTraining?, commercialLicensing?, contactPreference?, aiGeneration? }. signer is the DID when known, else the agent key. Values are the network's enum forms (e.g. CCBY, NotAllowed)

@flowsta/holochain - Desktop App Signing ​

For Holochain apps that run alongside Flowsta Vault. Signs via IPC - the user approves in Vault. An app that is linked to the person's identity (linkFlowstaIdentity, see the Holochain SDK) publishes to the Sign It network from their device.

signDocument(options) ​

Request Vault to sign a file hash. Shows an approval dialog in Vault with your app name, the file label and the first characters of the hash; when the signature will be published, also whose signing quota pays and a note that it publishes from this device. The dialog does not show the license, AI disclosure or intent you supplied.

typescript
import { signDocument } from '@flowsta/holochain';

const result = await signDocument({
  clientId: 'flowsta_app_abc123...',
  appName: 'ArtStudio',
  fileHash: 'a7f3b9c1e2d4...',
  label: 'Illustration.png',
  intent: 'authorship',
  contentRights: {
    license: 'cc-by',
    aiTraining: 'not_allowed',
    contactPreference: 'allow_contact_requests',
  },
});

if (result.published) {
  console.log('On the network:', result.actionHash);
} else {
  console.log('Signature returned to the app only:', result.signature);
}
ParameterTypeRequiredDescription
clientIdstringYesYour app's client ID from dev.flowsta.com
appNamestringYesShown in Vault approval dialog (for a linked app, the Vault shows the name approved at link time instead)
fileHashstringYesSHA-256 hex string (64 characters)
labelstringNoHuman-readable file name for the dialog
intentstringNoauthorship, approval, witness, receipt, agreement (Vault normalizes to the network's capitalized form, e.g. Authorship)
aiGenerationstringNonone, assisted, generated
contentRightsobjectNoSee Content Rights Object
publish (3.6.0)booleanNoPublish the signature to the Sign It network from the person's device. Default: true when this app is bound to an identity (it linked through linkFlowstaIdentity), false otherwise. Only linked apps may publish (PublishForbiddenError); every publish draws on a signing quota (QuotaExceededError)
ipcUrlstringNoDefault: sweeps 127.0.0.1:27777-27779 for the Vault (SDK v3 / auth 2.4)

Returns:

FieldTypeDescription
successbooleantrue if signed
fileHashstringThe hash that was signed
signaturestringBase64 Ed25519 signature
agentPubKeystringSigner's agent key (uhCAk... format)
signedAtstringISO 8601 timestamp
actionHashstring | nullHex action hash of the published record; null when the signature was not published
published (3.6.0)booleanWhether the signature was published to the Sign It network

Errors:

Error ClassCodeWhen
VaultNotFoundErrorvault_not_foundVault is not running
VaultBlockedError (3.1.0)vault_blockedThe browser refused the loopback request - the Vault may be running
VaultLockedErrorvault_lockedVault is locked
UserDeniedErroruser_deniedUser rejected the signing request
PublishForbiddenError (3.6.0)tier_forbiddenPublishing was asked for, but this app is not linked - only Flowsta pages and linked apps may publish. Link the app or sign with publish: false
QuotaExceededError (3.6.0)quota_exceededThe signing quota for this period is used up (the person's plan, and the sponsor pool when your app sponsors). description says which
IdentityMismatchError (v3)identity_mismatchThe Vault's active identity differs from the one this app is bound to - stop and tell the user
FlowstaHolochainErrortimeoutThe user didn't respond to the Vault dialog in time (~60 s)
FlowstaHolochainErrorinvalid_file_hash, reserved_prefix, conductor_not_ready, commit_failedMalformed hash; hash bytes beginning with flowsta-; the Vault is still starting; the publish could not be committed

SigningDnaNotInstalledError is deprecated: the Vault never emits its code, so it was never thrown. It is removed in 4.0.


getSigningStatus(ipcUrl?) ​

Check if Vault is available for document signing.

typescript
import { getSigningStatus } from '@flowsta/holochain';

const status = await getSigningStatus();
if (status.available) {
  // Show "Sign with Flowsta" button
}

Returns:

FieldTypeDescription
availablebooleantrue if Vault is running, unlocked, and ready to sign
vaultRunningbooleantrue if Vault IPC server is reachable
vaultUnlockedbooleantrue if Vault is unlocked

Content Rights Object ​

Both @flowsta/auth (2.6.0+) and @flowsta/holochain accept these keys and translate them to the snake_case the Vault and the API read:

FieldTypeValues
licensestringall-rights-reserved, cc0, cc-by, cc-by-sa, cc-by-nc, cc-by-nc-sa, mit, apache-2, gpl-3
commercialLicensingstringnot_available, open_to_licensing
aiTrainingstringallowed, allowed_with_attribution, requires_license, not_allowed
contactPreferencestringno_contact, allow_contact_requests

All fields are optional. Only declared fields are recorded in the signature. On the network (and in verification responses) these are stored in their enum forms - e.g. cc-by → CCBY, not_allowed → NotAllowed; the full mapping is on the Content Rights page. Send only the values listed: unknown strings are passed through unchanged, and the network's signing cell only accepts these values, so a publish with one fails.

Documentation licensed under CC BY-SA 4.0.