PRAEMUNIO API
    • Praemunio API - v1
    • AUTHENTICATION
      • API KEY Authentification
        POST
    • CORE
      • Teams
        • Members
          • Get Team Members
          • Add Team Member
          • Add Team Member Copy
        • Get Account Teams
        • Create Team
        • Get Team Details
        • Update Team Data
        • Delete Team
        • Get Team Data
        • Get Team workspaces
        • Get Team logs
      • Workspaces
        • Get Account Drive
        • Get Workspaces By Team
        • Create New Workspace
        • Get Workspace Details
        • Get Team Workspaces
        • Update workspace
        • Delete workspace
      • Folders
        • Get Folders
        • Create Folder
        • Get Folder Details
        • Get Folder Content
        • Update Folder
        • Delete Folder
        • Move Folder
      • Documents
        • Get Document Info
        • Get Document By Workspace
        • Update Document
        • Delete Document
        • Archive Document
        • Restore Document
        • Move Document
        • Stream Document
        • Stream Document Copy
        • Upload Document

    AUTHENTICATION

    Authentication & Webhooks#

    Praemunio API · Version 1 · Developer Documentation
    Integrate securely with the Praemunio platform: authenticate with API keys, manage short-lived access tokens, control access with scopes, and receive real-time event notifications through signed webhooks.
    Base URLhttps://api.praemunio.io/api/v1
    FormatJSON (Content-Type: application/json)
    AuthenticationAPI Key Pair → JWT Bearer token
    Documentationdocs.praemunio.io

    Table of contents#

    1.
    Overview
    2.
    Quick start
    3.
    API keys, scopes & rate limits
    4.
    Authentication endpoints
    5.
    Token security model
    6.
    Webhooks
    7.
    Verifying webhook signatures
    8.
    Client integration example
    9.
    Errors
    10.
    Security best practices

    1. Overview#

    The Praemunio API relies on two building blocks:
    Authentication
    You exchange an API key pair (publicKey + secretKey) for a token pair:
    TokenTypeLifetimeUsage
    access_tokenJWT15 minutesSent in the Authorization: Bearer <access_token> header on every request
    refresh_tokenOpaque, single-use7 daysExchanged for a new token pair (automatic rotation)
    Webhooks
    Praemunio sends HTTP POST notifications to your endpoint when events occur on the platform (for example document.created, workspace.updated). Every delivery is signed with HMAC-SHA256, carries idempotency headers, is logged, and is automatically retried on failure.

    Authentication flow#


    2. Quick start#

    Step 1 — Authenticate
    Step 2 — Call the API with your access token
    Step 3 — Renew tokens before they expire
    Always store the new refresh_token. Each refresh token can be used only once.

    3. API keys, scopes & rate limits#

    3.1 Credentials#

    CredentialFormatDescription
    publicKeypk_live_… / pk_test_…Public identifier of the key
    secretKeysk_live_… / sk_test_…Private secret. Shown only once at creation. Praemunio stores only a one-way hash and cannot recover it.
    pk_test_ / sk_test_ keys target the test environment; pk_live_ / sk_live_ keys target production.
    Never expose a secretKey in client-side code, public repositories, or logs.
    If a secret is lost or compromised, disable the key and create a new one from your dashboard.
    Webhook secrets (whsec_…) are generated when a webhook is created, are stored encrypted, and are also shown only once. You use them to verify the signature of incoming deliveries (see section 7).

    3.2 Scopes & permissions#

    Each API key carries granular permissions expressed as resource:action scopes.
    ResourceScopes
    Documentsdocument:read, document:write, document:delete, document:share
    Foldersfolder:read, folder:write, folder:delete
    Workspacesworkspace:read, workspace:manage
    Teamsteam:read, team:manage
    Sharesshare:read, share:write
    Webhookswebhook:manage
    A request made with a key that lacks the required scope is rejected with 403 Forbidden and the error code insufficient_scopes. Grant each key only the scopes it needs (principle of least privilege).

    3.3 Rate limits & quotas#

    Each API key enforces two configurable ceilings:
    LimitFieldDefault
    Hourly rate limitrateLimit1,000 requests / hour
    Daily quotadailyRequestLimit10,000 requests / day
    Rate limit information is returned on API responses:
    HeaderDescription
    X-RateLimit-LimitMaximum number of requests allowed in the current window
    X-RateLimit-RemainingRequests remaining in the current window
    X-RateLimit-ResetTime at which the window resets, as a Unix timestamp (seconds)
    When a limit is exceeded, the API returns 429 Too Many Requests with the error code rate_limit_exceeded. Wait until X-RateLimit-Reset before retrying.

    3.4 Pagination#

    List endpoints (currently GET /webhooks and GET /webhooks/:id/logs) accept the following query parameters:
    ParameterDefaultMaximumDescription
    page1—Page number
    limit20100Items per page
    GET /api/v1/webhooks?page=2&limit=50

    4. Authentication endpoints#

    All responses use the following envelope:

    4.1 Authenticate — POST /auth/api-key#

    Exchanges an API key pair for an access token and a refresh token.
    Request body
    FieldTypeRequiredDescription
    publicKeystringYesYour public key (pk_…)
    secretKeystringYesYour secret key (sk_…)
    {
      "publicKey": "pk_live_a1b2c3d4e5f67890",
      "secretKey": "sk_live_9876543210fedcba9876543210fedcba"
    }
    Response — 200 OK
    {
      "success": true,
      "message": "Authentication successful",
      "result": {
        "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
        "refresh_token": "rt_8f1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a...",
        "token_type": "Bearer",
        "expires_in": 900,
        "refresh_expires_in": 604800
      },
      "statusCode": 200
    }
    FieldDescription
    token_typeAlways Bearer
    expires_inAccess token lifetime, in seconds
    refresh_expires_inRefresh token lifetime, in seconds
    Errors — 401 Unauthorized
    errors.codeMessageCause
    invalid_credentialsInvalid credentialsUnknown key or wrong secret
    api_key_disabledAPI key is disabledThe key has been disabled
    api_key_expiredAPI key has expiredThe key's expiration date has passed
    Example:
    {
      "success": false,
      "message": "API key is disabled",
      "result": null,
      "errors": { "code": "api_key_disabled" },
      "statusCode": 401
    }

    4.2 Refresh tokens — POST /auth/refresh-token#

    Issues a new token pair from a valid refresh token. The submitted refresh token is consumed and can no longer be used.
    Request body
    FieldTypeRequiredDescription
    refresh_tokenstringYesThe current refresh token
    {
      "refresh_token": "rt_8f1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a..."
    }
    Response — 200 OK
    {
      "success": true,
      "message": "Token refreshed successfully",
      "result": {
        "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
        "refresh_token": "rt_9e8d7c6b5a4f3e2d1c0b9a8f7e6d5c4b3a2f1e0d...",
        "token_type": "Bearer",
        "expires_in": 900,
        "refresh_expires_in": 604800
      },
      "statusCode": 200
    }
    Errors — 401 Unauthorized
    errors.codeCauseAction
    invalid_grantRefresh token invalid or revokedRe-authenticate with your API key
    refresh_token_expiredRefresh token older than 7 daysRe-authenticate with your API key
    refresh_token_reuse_detectedA consumed refresh token was replayed outside the 30-second grace period; the whole session is revoked (see section 5)Re-authenticate with your API key
    {
      "success": false,
      "message": "Refresh token reuse detected",
      "result": null,
      "errors": { "code": "refresh_token_reuse_detected" },
      "statusCode": 401
    }

    4.3 Validate a key — POST /auth/validate-key#

    Validates a raw secretKey and returns the key's metadata.
    Request body
    FieldTypeRequiredDescription
    keystringYesThe secret key to validate
    {
      "key": "sk_live_9876543210fedcba9876543210fedcba"
    }
    Response — 200 OK
    {
      "success": true,
      "message": "API key is valid",
      "result": {
        "id": 42,
        "publicId": "key_987654321",
        "accountId": 10,
        "name": "Production Service Key",
        "publicKey": "pk_live_a1b2c3d4e5f67890",
        "scopes": ["document:read", "document:write"],
        "allowedOrigins": ["https://app.praemunio.io"],
        "environment": "production",
        "isActive": true,
        "expiresAt": null,
        "lastUsedAt": "2026-09-30T07:30:00.000Z",
        "createdAt": "2026-01-15T10:00:00.000Z",
        "updatedAt": "2026-09-30T07:30:00.000Z"
      },
      "statusCode": 200
    }
    FieldDescription
    scopesScopes granted to the key
    allowedOriginsOrigins allowed to use the key
    environmentproduction or test
    isActiveWhether the key is currently enabled
    expiresAtExpiration date (ISO 8601), or null if the key does not expire
    lastUsedAtLast time the key was used
    Error — 401 Unauthorized
    {
      "success": false,
      "message": "Invalid API key",
      "result": null,
      "errors": { "code": "invalid_api_key" },
      "statusCode": 401
    }

    5. Token security model#

    MechanismBehavior
    Refresh token rotationEvery refresh token is single-use. Each refresh returns a new access token and a new refresh token.
    Grace period (30 s)If a refresh request is retried within 30 seconds of the first one (for example after a network timeout), it is tolerated and the session is preserved.
    Reuse detectionReplaying a consumed refresh token outside the grace period is treated as potential theft: all tokens of that session family are revoked immediately. Your client must re-authenticate with its API key.
    Real-time key statusDisabling, deleting, or expiring an API key takes effect at the next token refresh.

    6. Webhooks#

    Webhooks let you register HTTPS endpoints that receive real-time notifications about events in your account.

    6.1 Endpoints#

    MethodEndpointDescription
    GET/webhooks?page=1&limit=20List all webhooks (paginated)
    POST/webhooksCreate a webhook (returns its secret once)
    GET/webhooks/:idGet webhook details
    PUT/webhooks/:idUpdate a webhook
    DELETE/webhooks/:idDelete a webhook (soft delete)
    POST/webhooks/:id/testSend a test delivery (webhook.test event)
    GET/webhooks/:id/logs?page=1&limit=20Retrieve delivery logs (paginated)
    POST/webhooks/:id/pausePause deliveries
    POST/webhooks/:id/resumeResume deliveries
    Webhook management requires the webhook:manage scope.

    6.2 Create a webhook — POST /webhooks#

    Request body
    FieldTypeRequiredDescription
    namestringYesDisplay name
    urlstringYesHTTPS endpoint receiving the events
    eventsstring[]YesEvents to subscribe to (see 6.4)
    environmentstringYesproduction or test
    descriptionstringNoFree-form description
    maxRetriesintegerNoMaximum number of retry attempts after a failed delivery
    retryIntervalintegerNoDelay between retries, in seconds (default: 10). The delay is fixed (no exponential backoff).
    {
      "name": "Production Webhook",
      "url": "https://example.com/api/webhooks",
      "events": ["document.created", "document.updated", "workspace.storage_updated"],
      "environment": "production",
      "description": "Document event notifications",
      "maxRetries": 3,
      "retryInterval": 10
    }
    Response — 201 Created
    {
      "success": true,
      "message": "Webhook created successfully",
      "result": {
        "id": 12,
        "publicId": "wh_a1b2c3d4-e5f6-7890-abcd-1234567890ab",
        "name": "Production Webhook",
        "url": "https://example.com/api/webhooks",
        "events": ["document.created", "document.updated", "workspace.storage_updated"],
        "secret": "whsec_3f8a9b2c1d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a",
        "environment": "production",
        "isActive": true,
        "createdAt": "2026-09-30T07:00:00.000Z"
      },
      "statusCode": 201
    }
    Important: the secret is returned only once. Store it securely; you need it to verify signatures.

    6.3 Delivery#

    When a subscribed event occurs, Praemunio sends an HTTP POST request to your endpoint.
    Headers
    HeaderExampleDescription
    Content-Typeapplication/jsonBody format
    X-Webhook-IDwh_a1b2c3d4-e5f6-7890-abcd-1234…Identifier of the registered webhook
    X-Webhook-Delivery-IDdel_9f8e7d6c-5b4a-3f2e-1d0c-9b8a7f…Unique identifier of this delivery attempt (also available as deliveryId in the body)
    X-Webhook-Eventdocument.createdEvent name
    X-Webhook-Signaturet=1700000000,v1=a1b2c3d4e5f6…HMAC-SHA256 signature
    Signature
    signed_payload = "<timestamp>.<raw_request_body>"
    v1             = HEX( HMAC_SHA256(webhook_secret, signed_payload) )
    Expected response
    Respond with any 2xx status as quickly as possible. Perform heavy processing asynchronously after acknowledging the request.
    Retries and logs
    A delivery is considered failed on a network error or an HTTP status >= 400.
    Failed deliveries are retried up to maxRetries times, spaced by a fixed retryInterval (in seconds).
    Every attempt is recorded (status, HTTP code, latency, response body) and available through GET /webhooks/:id/logs.

    6.4 Event catalog#

    CategoryEventTriggered when
    Documentsdocument.createdA document is uploaded or created
    document.updatedA document's content or metadata is updated
    document.deletedA document is deleted
    document.archivedA document is archived
    document.restoredAn archived document is restored
    document.movedA document is moved to another folder
    document.sharedA document is shared via link or user invitation
    Foldersfolder.createdA folder is created
    folder.updatedA folder is renamed or updated
    folder.deletedA folder is deleted
    folder.movedA folder is moved
    Workspacesworkspace.createdA workspace is created
    workspace.updatedA workspace is modified
    workspace.deletedA workspace is deleted
    workspace.storage_updatedA workspace's storage quota is modified
    Teamsteam.createdA team is created
    team.updatedA team is updated
    team.deletedA team is deleted
    team.member_addedA member is added to a team
    team.member_removedA member is removed from a team
    team.invitation_sentA team invitation is sent
    team.invitation_acceptedA team invitation is accepted
    Sharesshare.createdShare access is created
    share.deletedShare access is revoked
    Usageusage.quota_exceededAn account quota or limit is exceeded
    Systemwebhook.testYou trigger a test delivery via /webhooks/:id/test

    6.5 Event payload#

    Every delivery has the same JSON envelope; the data object depends on the event.
    Example — document.created
    {
      "id": "evt_9b1deb4d-3b7d-4bad-9bdd-2b0d7b3d0b1a",
      "deliveryId": "del_a1b2c3d4-e5f6-7890-abcd-1234567890ab",
      "event": "document.created",
      "timestamp": "2026-09-30T08:00:00.000Z",
      "data": {
        "documentId": "doc_8849201",
        "name": "Q3_Financial_Report.pdf",
        "size": 2485910,
        "mimeType": "application/pdf",
        "folderId": "fol_1029",
        "workspaceId": "ws_55",
        "createdBy": {
          "accountId": 42,
          "email": "user@company.com"
        }
      }
    }

    6.6 Idempotency#

    Because of retries, the same event may be delivered more than once. Your handler must be idempotent.
    Recommended deduplication flow:
    1.
    Read the event identifier (id in the body, e.g. evt_...), which identifies the event itself.
    2.
    Look it up in a cache or database (for example Redis) holding the identifiers processed during the last 24 hours.
    3.
    If it was already processed, return 200 OK immediately without re-running your business logic.
    4.
    Otherwise process the event, then record the identifier.
    X-Webhook-Delivery-ID / deliveryId identifies a single delivery attempt. Use it for logging and support requests, and to correlate with the entries returned by GET /webhooks/:id/logs.

    7. Verifying webhook signatures#

    Always verify the signature before processing a delivery. The check confirms the request comes from Praemunio and protects against replay attacks.
    Verification steps
    1.
    Parse X-Webhook-Signature to extract the timestamp (t) and the signature (v1).
    2.
    Reject the request if the timestamp is older than your tolerance window (300 seconds recommended).
    3.
    Recompute the HMAC-SHA256 of "<t>.<raw_body>" with your whsec_… secret.
    4.
    Compare with v1 using a constant-time comparison.
    Use the raw request body. Re-serializing a parsed JSON object can change whitespace or key order and break the signature.
    Node.js / TypeScript (Express)

    8. Client integration example#

    A TypeScript client that authenticates, refreshes tokens proactively, avoids concurrent refresh calls (refresh tokens are single-use, so parallel refreshes would trigger reuse detection), and respects rate limits.
    Usage

    9. Errors#

    HTTP statuserrors.codeCauseRecommended action
    200 OK—SuccessProcess the response
    201 Created—Resource createdStore any generated secret
    400 Bad Requestinvalid_requestMalformed or invalid requestFix the request parameters
    401 Unauthorizedinvalid_credentialsIncorrect public key or secret keyVerify your credentials
    401 Unauthorizedinvalid_api_keyKey rejected by /auth/validate-keyVerify the key
    401 Unauthorizedapi_key_disabledKey disabled by an administratorRe-enable the key in the console
    401 Unauthorizedapi_key_expiredKey expiration date passedGenerate a new key
    401 Unauthorizedinvalid_grantRefresh token invalid or revokedRe-authenticate with your API key
    401 Unauthorizedrefresh_token_expiredRefresh token expired (older than 7 days)Re-authenticate with your API key
    401 Unauthorizedrefresh_token_reuse_detectedConsumed refresh token replayedDiscard all tokens and re-authenticate
    403 Forbiddeninsufficient_scopesRequired scope missing on the keyAdd the required scopes to the key
    404 Not Foundnot_foundResource does not existCheck the requested identifier
    429 Too Many Requestsrate_limit_exceededHourly rate limit or daily quota exceededWait until X-RateLimit-Reset
    500 Internal Server Errorinternal_errorUnexpected server errorRetry later; contact support if it persists

    10. Security best practices#

    Keep secrets server-side. Load secretKey and whsec_ values from environment variables or a secrets manager, never from source code.
    Apply least privilege. Grant each key only the scopes it needs, and use separate keys per environment (test vs live) and per application.
    Rotate keys regularly and disable any key you suspect is exposed.
    Persist the latest refresh token atomically after every refresh; losing it forces a new authentication.
    Serialize token refreshes in multi-threaded or multi-instance clients.
    Respect rate limits and back off on 429 responses.
    Verify every webhook signature and enforce the timestamp tolerance.
    Make webhook handlers idempotent and answer quickly with a 2xx.
    Use HTTPS endpoints only for webhooks.

    Support#

    Questions or issues? Contact the Praemunio team through docs.praemunio.io.
    Modified at 2026-09-30 08:07:59
    Previous
    Praemunio API - v1
    Next
    API KEY Authentification
    Built with