Web application for managing digital signatures.
Other electronic document signing platforms often lack support for advanced or qualified electronic signatures available under the EU's eIDAS standard, relying instead on simpler signatures without stronger legal validity. Our eIDAS-compatible signing portal addresses this gap by providing an open-source user-friendly platform for creating qualified electronic signatures using government-issued eID cards and other qualified signature creation devices.
Unlike existing alternatives, our project will integrate seamlessly with desktop and mobile signer applications, both open-source and commercial, enabling intuitive qualified document signing, validation, archiving, and API integration with third-party systems. Instances are planned to behave federatively and exchange documents simplifying the adoption of qualified electronic signatures across Europe, reducing reliance on proprietary solutions, and improving digital administrative workflows.
Demo: agp.dev.slovensko.digital
This project is funded through NGI Zero Core, a fund established by NLnet with financial support from the European Commission's Next Generation Internet program. Learn more at the NLnet project page.
Autogram Portal provides comprehensive electronic signature management for individuals, organizations, and integrators. The user guide (Slovak and English) is part of the app at /docs.
- Sign Documents - Upload a document and sign it with a qualified electronic signature using Autogram Desktop, Autogram Mobile, eIdentita or a standalone application such as Podpisuj
- Verify Signatures - Validate existing electronic signatures
- View Document Contents - Visualize ASiC-E containers and Slovak XML Datacontainers (special XML format for eGovernment)
- Sign Requests - Sign or decline documents sent to you through an email invitation link
Sign in via Google OAuth2 or email magic link to access:
- Organizations - Every user has a personal Basic organization; PRO organizations have several members sharing all documents and bundles
- Request Signatures - Send documents in bundles to recipients by email or a shared link, with signing rules (all, any, a number of recipients) and email notifications
- Verified AdES Signing - Let recipients sign with a simple electronic signature backed by an evidence record after SMS or email verification
- Visible Signature Fields - Prepare a visible PAdES signature field for each recipient of a PDF
- Extend Signatures - Add qualified timestamps (T) or archive timestamps (LTA) to existing signatures
- Validation Archive - Keep validation metadata of signed documents and refresh them to LTA before they expire (organization feature)
- Plan Usage - See the usage of the organization's plan limits
- Create Bundles - Programmatically create bundles of contracts via API
- Embed Signing - Integrate signing UI into your app using JavaScript SDK
- Webhook Notifications - Receive real-time updates when documents are signed
- Manage Documents - Upload, track, download, and delete signed documents
- Ruby on Rails 8
- PostgreSQL
# Setup
cp .env.sample .env
bundle install
bin/rails db:setup
# Run
bin/devVisit http://localhost:3000
We are using GoodJob for job queue. Dashboard is available at /admin/good_job for authenticated admin users.
Edit .env file:
API_SKIP_AUTH=true- Skip auth in developmentAUTOGRAM_SERVICE_URL- Autogram service URLAVM_URL- Autogram AVM service URLPRICING_URL- Link to the full price list shown in the homepage pricing section (hidden when blank)WEBHOOK_REQUIRE_HTTPS=true- Require HTTPS for webhook destinations (defaults to true in production)WEBHOOK_ALLOWED_HOSTS=host1,host2- Optional allowlist override for internal webhook destinationsWEBHOOK_OPEN_TIMEOUT=3- Outbound webhook TCP connect timeout in secondsWEBHOOK_TIMEOUT=5- Outbound webhook request timeout in seconds
Usage limits of the organization plans are configured per instance (app/lib/plan_limits.rb).
A blank or missing value means unlimited; only BASIC_MAX_MEMBERS defaults to 1.
Variable (BASIC_ / PRO_ prefix) |
Meaning |
|---|---|
*_MAX_MEMBERS |
Members of an organization |
*_MAX_STORED_DOCUMENTS |
Documents (contracts, standalone or in bundles) stored at once |
*_STORAGE_GB |
Total size of stored documents and their signed versions |
*_MONTHLY_SIGNATURE_REQUESTS |
Documents sent for signature per calendar month |
*_MONTHLY_TIMESTAMPS |
Timestamps per calendar month (signature extension, archivation, timestamped AVM signatures) |
*_RETENTION_DAYS |
Documents and bundles are deleted this many days after they were created |
| Variable | Default | Meaning |
|---|---|---|
MAX_DOCUMENT_SIZE_MB |
unlimited | Total size of the files of one document, also for anonymous uploads |
ANONYMOUS_RETENTION_MINUTES |
55 | Anonymous documents are deleted after this time (the cleanup runs every 5 minutes) |
PLAN_CHANGE_RETENTION_GRACE_DAYS |
30 | After a plan change (e.g. a cancelled PRO), documents are kept this long before the new plan's retention applies |
SIGNED_HISTORY_DAYS |
unlimited | In a plan with retention, documents signed for others stay in the received list this long |
A document sent for signature counts once: when a recipient is notified, or, for bundles shared by
a link or created through the API, when someone other than the organization signs the bundle
(all its documents count then). Monthly usage is kept in usage_records, so deleting documents does
not return the quota; the records are also the billing basis shown to admins on the organization page.
Organizations see their usage in their settings and on the dashboard; integrators can call
GET /api/v1/usage.
# Tests
bin/rails test
# Code quality
bin/rubocop
bin/brakemanWe are using kamal for deployment.
Main branch is automatically deployed to staging at agp.dev.slovensko.digital
See LICENSE file.
Autogram Portal provides a complete API solution for applications that need electronic signature capabilities without managing the complexity of digital signing infrastructure.
- API Documentation - /api/v1 (Swagger UI) or
public/openapi.yaml - SDK Examples - public/sdk-example.html
- API Environment - Set
API_SKIP_AUTH=truein.envto skip JWTs in local development - Process documentation - /docs
- Federation architecture - docs/federation.md
- Create Contracts - Your app sends documents via API to create a Bundle of Contracts
- Each Bundle can contain one or more Contracts (typically one)
- Each Contract can contain multiple Documents (rare, only if needed for single ASiC-E container)
- You receive UUIDs for the bundle and contracts to track them
- Get Notified - When signing is complete, you're notified via:
- Webhooks - Receive callbacks at your specified URL (using Standard Webhooks specification)
- Polling - Check bundle/contract status via API
- Download Results - Retrieve signed files and optionally delete records from the portal
Choose how end users will sign:
Option A: Redirect to Portal
- Send users a link to the Autogram Portal
- They complete the entire signing process on our platform
- No integration needed beyond API calls
Option B: Embedded Signing (SDK)
- Embed signing interface in your app using our JavaScript SDK
- Display as iframe (embedded) or popup overlay
- See sdk-example.html for complete implementation examples
- Supports both individual contracts and bundles
Option C: Email Notifications (planned)
- Specify recipients when creating bundle/contract
- Portal automatically sends emails with signing links and instructions
Autogram Portal can route signing requests across portal instances. The current implementation uses two different trust decisions:
- sender-side routing is explicit, because a sender chooses a trusted home portal for a federated recipient
- recipient-side pasted-link opening is dynamic, because a recipient can paste an origin link into their own portal even if that origin portal was not preconfigured locally
How it works
- Admins register the home portals that local senders are allowed to target when creating federated recipients.
- The sender creates a bundle and assigns a recipient either to the local portal or to one of those trusted remote home portals.
- The origin portal still creates the canonical signing link, typically
/bundles/:bundle_uuid/sign?recipient=:recipient_uuid. - The recipient can either open that link directly on the origin portal or paste it into their own portal at
/federation/requests/open. - The recipient's home portal discovers the origin portal from the pasted URL, loads
/.well-known/autogram-portal.json, and checks that the origin supports the federation preview and claim APIs. - The home portal asks the origin portal for a preview of the request and, after local sign-in, sends a signed federation claim request.
- The origin portal verifies that the claiming home portal matches the portal assigned to that recipient. If accepted, it returns a short-lived sign URL containing
grant=.... - The recipient is redirected back to the origin portal, which validates the grant and continues with the standard signing flow.
Which requests are used
- Origin request URL - The normal bundle signing link used in emails and copied links.
- Metadata document -
/.well-known/autogram-portal.jsonexposes issuer, portal name, public key, federation API base URL, supported capabilities, and optional email-domain hints. - Preview request -
GET /api/federation/v1/requests/:recipient_uuidfetches a user-facing preview of a pasted origin request. - Claim request -
POST /api/federation/v1/requests/:recipient_uuid/claimexchanges the locally authenticated user for a short-lived origin sign URL. - Grant-backed sign URL - The claim response returns the actual signing URL with
grant=.... This token is short-lived and tied to a stored access-grant record.
Security model
- Sender-side routing uses an explicitly configured trusted portal list. This is how the origin portal decides which home portal is allowed to claim a federated recipient.
- Recipient-side pasted-link opening is more permissive: the home portal may dynamically discover an origin portal from its metadata document without a preconfigured local trust record.
- Portal-to-portal requests use a dedicated JWT assertion with
iss,aud,exp,jti, andscopeclaims. - The origin portal remains the source of truth for recipient state, signing rules, bundle completion, and signed documents, and it still decides which home portal is allowed to claim each federated recipient.
- Access grants are revoked when the recipient is withdrawn or no longer needed because the bundle has already completed.
Current scope
The current implementation lets a recipient paste and claim a foreign request on their own portal even if the origin portal was not preconfigured locally, but the actual signing still completes on the origin portal.
FEDERATION_BASE_URL- Public base URL this portal advertises to peer portals; when blank, the federation section is hidden from the docs pageFEDERATION_ISSUER- Stable issuer identifier used in federation metadata and JWT assertionsFEDERATION_PORTAL_NAME- Display name exposed in federation metadataFEDERATION_PUBLIC_KEY_PEM- Public key advertised to peer portalsFEDERATION_PRIVATE_KEY_PEM- Private key used to sign outgoing federation assertionsFEDERATION_EMAIL_DOMAINS- Optional comma-separated email domains advertised in federation metadata
1. Authentication
API uses RS256 JWT tokens. See API documentation for authentication details.
2. Create a Bundle
curl -X POST https://agp.dev.slovensko.digital/api/v1/bundles \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_JWT_TOKEN" \
-d '{
"id": "500927d7-47fa-43fd-bda4-547e703a3a4b",
"contracts": [
{
"allowedMethods": ["qes"],
"documents": [
{
"filename": "contract.txt",
"content": "Sample text content",
"contentType": "text/plain"
}
],
"signatureParameters": {
"format": "XAdES",
"container": "ASiC_E"
}
}
],
"webhook": {
"url": "https://example.com/webhook"
}
}'3. Integrate Signing UI (optional)
// load Autogram Portal SDK.js
<script src="https://agp.dev.slovensko.digital/sdk.js"></script>
<script>
// Show bundle in popup mode
agp.initBundleIframe('500927d7-47fa-43fd-bda4-547e703a3a4b', {
mode: 'popup',
popupTitle: 'Sign Document'
});
// Or embedded in your page
agp.initBundleIframe('500927d7-47fa-43fd-bda4-547e703a3a4b', {
mode: 'iframe',
parentElement: '#signing-container',
height: '600px',
title: 'Sign the employment contract' // accessible name of the iframe (optional)
});
</script>Made by slovensko.digital
