Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.spitshake.io/llms.txt

Use this file to discover all available pages before exploring further.

DocuTrust supports three authentication methods. Choose the one that fits your integration pattern.
MethodHeaderBest for
API TokenX-Auth-Token: TOKENServer-side integrations, scripts, CI/CD pipelines
Session CookieCookie: _docutrust_session=VALUEBrowser-based access after web login
JWT BearerAuthorization: Bearer TOKENEmbedded signing forms and template builders
API tokens are the primary way to authenticate server-side API calls. Each token is scoped, rotatable, and tied to a specific user account.

Creating a token

Navigate to Settings > API in the DocuTrust dashboard and click Create Token. You can also create tokens programmatically.
curl -X POST "$DOCUTRUST_URL/api/access_tokens" \
  -H "X-Auth-Token: $DOCUTRUST_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Production Backend",
    "scopes": ["templates:read", "templates:write", "submissions:read", "submissions:write"],
    "allowed_ips": ["203.0.113.0/24"],
    "expires_at": "2027-04-08T00:00:00.000Z"
  }'
Response:
{
  "id": 15,
  "name": "Production Backend",
  "user_id": 3,
  "token": "dt_live_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0",
  "token_prefix": "dt_live_a1b2c3d4",
  "scopes": ["templates:read", "templates:write", "submissions:read", "submissions:write"],
  "allowed_ips": ["203.0.113.0/24"],
  "expired": false,
  "expires_at": "2027-04-08T00:00:00.000Z",
  "last_used_at": null,
  "created_at": "2026-04-08T10:00:00.000Z"
}
The full token value is only returned once at creation time. Store it securely — you cannot retrieve it again. The token_prefix is always visible for identification purposes.

Using the token

Pass the token in the X-Auth-Token header with every request:
curl https://spitshake.io/api/templates \
  -H "X-Auth-Token: dt_live_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0"

Available scopes

Scopes control which API operations a token can perform. Assign the minimum set of scopes your integration needs.
ScopeAllows
templates:readList and retrieve templates, download template documents and thumbnails
templates:writeCreate, update, clone, merge, and archive templates
submissions:readList and retrieve submissions, list and download documents
submissions:writeCreate submissions (single and bulk), archive submissions
users:readList and retrieve user accounts, view current user
submissions:draftPrepare draft submissions (create, update). No emails sent.
submissions:sendSend submissions for signing (legally binding). Dispatches invitation emails.
submissions:remindSend signing reminders to pending submitters.
submitters:writeUpdate submitter information and resend invitations.
audit_events:readRead the account-wide audit trail.
A token without any scopes is rejected on all endpoints. Always assign at least one scope.

IP allowlisting

Restrict a token to a set of IP addresses or CIDR ranges. Requests from any other IP will receive a 403 Forbidden response. Leave allowed_ips empty (or omit it) to allow requests from any IP.
{
  "allowed_ips": ["203.0.113.10", "198.51.100.0/24", "2001:db8::/32"]
}

Revoking a token

Revoke a token immediately when it is compromised or no longer needed:
curl -X POST "$DOCUTRUST_URL/api/access_tokens/15/revoke" \
  -H "X-Auth-Token: $DOCUTRUST_TOKEN"
Revoked tokens are rejected immediately on all subsequent requests. This action cannot be undone.

Listing tokens

Retrieve all tokens for your account. The full token value is never returned — only the token_prefix.
curl "$DOCUTRUST_URL/api/access_tokens" \
  -H "X-Auth-Token: $DOCUTRUST_TOKEN"
Response:
{
  "data": [
    {
      "id": 15,
      "name": "Production Backend",
      "user_id": 3,
      "token_prefix": "dt_live_a1b2c3d4",
      "scopes": ["templates:read", "templates:write", "submissions:read", "submissions:write"],
      "allowed_ips": ["203.0.113.0/24"],
      "expired": false,
      "expires_at": "2027-04-08T00:00:00.000Z",
      "last_used_at": "2026-04-08T14:32:00.000Z",
      "created_at": "2026-04-08T10:00:00.000Z"
    },
    {
      "id": 8,
      "name": "CI Pipeline",
      "user_id": 3,
      "token_prefix": "dt_live_x9y8z7w6",
      "scopes": ["submissions:read"],
      "allowed_ips": [],
      "expired": false,
      "expires_at": null,
      "last_used_at": "2026-04-07T09:15:00.000Z",
      "created_at": "2026-03-01T08:00:00.000Z"
    }
  ],
  "pagination": {
    "count": 2,
    "next": null,
    "prev": null
  }
}
When a user logs in through the DocuTrust web interface, the server sets a _docutrust_session cookie. This cookie authenticates all subsequent requests from the browser automatically. Session cookies are primarily used by the DocuTrust frontend. They are useful if you are building a custom frontend that shares the same domain or if you need to make authenticated requests from browser JavaScript after a user has logged in.

How it works

  1. The user logs in at https://spitshake.io/sign_in with email and password (plus MFA if enabled).
  2. The server returns a Set-Cookie: _docutrust_session=VALUE header.
  3. The browser includes this cookie on all subsequent requests to the DocuTrust domain.
// Browser-side fetch (cookie is sent automatically)
const response = await fetch("https://spitshake.io/api/templates", {
  credentials: "include",
});

const data = await response.json();

Session lifetime

  • Sessions expire after 30 minutes of inactivity.
  • Security events (password change, MFA disable, privilege escalation) immediately invalidate all active sessions for the affected user.
  • Logging out destroys the session server-side.
Do not use session cookies for server-to-server integrations. They are tied to browser state and will expire. Use API tokens instead.

JWT Bearer Token

JWT bearer tokens are designed for embedded components — signing forms and template builders rendered inside your application using an iframe or the DocuTrust JavaScript SDK.

Generating a token

Request a short-lived JWT from the embed token endpoint. This call must be made server-side using an API token.
curl -X POST "$DOCUTRUST_URL/api/embed/token" \
  -H "X-Auth-Token: $DOCUTRUST_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "resource_type": "submission",
    "resource_id": 187,
    "permissions": ["sign", "view"]
  }'
Response:
{
  "token": "eyJhbGciOiJIUzI1NiJ9.eyJyZXNvdXJjZV90eXBlIjoic3VibWlzc2lvbiIsInJlc291cmNlX2lkIjoxODcsInBlcm1pc3Npb25zIjpbInNpZ24iLCJ2aWV3Il0sImV4cCI6MTcxMjU5NTIwMH0.abc123signature",
  "expires_at": "2026-04-08T14:00:00.000Z"
}

Using the token

Pass the JWT in the Authorization header:
curl "$DOCUTRUST_URL/api/embed/submission/187" \
  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiJ9..."
Or pass it to the DocuTrust JavaScript SDK when initializing an embedded component:
<div id="docutrust-signing"></div>
<script src="https://spitshake.io/embed/sdk.js"></script>
<script>
  DocuTrust.embed({
    target: "#docutrust-signing",
    token: "eyJhbGciOiJIUzI1NiJ9...",
    type: "signing",
  });
</script>

Token properties

PropertyValue
AlgorithmHS256
Expiry4 hours from creation
RenewableNo — generate a new token when the current one expires
ScopeLimited to the specific resource_type and resource_id
JWT tokens can only access the specific resource they were created for. A token scoped to submission 187 cannot access submission 188.

OAuth 2.1 (Authorization Code + PKCE)

For connector directories and third-party AI agents. OAuth provides delegated access — users authorize your application without sharing their API token.

Discovery

GET /.well-known/oauth-authorization-server
Returns authorization, token, and revocation endpoints, supported scopes, and PKCE methods.

Flow

  1. Register your client via POST /oauth/register (Dynamic Client Registration) or the admin UI at /oauth/applications
  2. Redirect the user to /oauth/authorize with response_type=code, client_id, redirect_uri, scope, code_challenge (S256), and code_challenge_method
  3. Exchange the authorization code at POST /oauth/token with grant_type=authorization_code, code, redirect_uri, client_id, and code_verifier
  4. Use the access token as Authorization: Bearer <token> on any API or MCP endpoint
  5. Refresh with grant_type=refresh_token when the access token expires (1 hour)
PKCE is mandatory for all clients (OAuth 2.1 requirement). Consent is always required.

Error responses

Authentication failures return standard HTTP error codes:
StatusMeaningCommon cause
401 UnauthorizedMissing or invalid credentialsNo token provided, token revoked, token expired, invalid JWT
403 ForbiddenValid credentials but insufficient permissionsToken scope does not include the required permission, IP not in allowlist
429 Too Many RequestsRate limit exceededMore than 120 requests per minute from the same token
Example 401 response:
{
  "error": "unauthorized",
  "message": "Invalid or expired API token. Verify your X-Auth-Token header.",
  "status": 401
}
Example 403 response:
{
  "error": "forbidden",
  "message": "Token does not have the required scope: templates:write",
  "status": 403
}

Security best practices

Assign only the scopes your integration actually needs. A webhook consumer that only reads submission status should not have templates:write.
Set an expires_at date when creating tokens. Create a new token before the old one expires, update your integration, then revoke the old token.
Lock your production tokens to your server’s IP range. This prevents stolen tokens from being used outside your infrastructure.
API tokens must only be used in server-side code. For browser-based integrations, use JWT bearer tokens generated by your backend.
Review the last_used_at timestamp on your tokens. Revoke any token that has not been used in a long time or that you do not recognize.
If your account has MFA enforcement enabled, all users must verify a TOTP code at login. This protects against credential theft that could lead to token creation by an attacker.

API Versioning

All API responses include an X-API-Version: v1 header. The API is available at both /api/* and /api/v1/* (explicit version pin). Future breaking changes will be introduced under /api/v2.