API Driven Solution
Integrate Your Systems and Automate Workflows with the Enlivy API
Connect Enlivy to your other tools and platforms through its API, and build integrations that keep up as your business grows.
Get your API keyFree account, no card needed.
API-FIRST BY DESIGN
Built for Developers. Ready for Business.
Everything you can do in Enlivy, you can do through the API, within your role and your plan's limits.
The Enlivy app runs on the same API, so what you do in the interface you can script, within your role, the organizations your token is limited to, and your plan's limits. Automate repetitive tasks, connect Enlivy with your existing systems, and build workflows that match how your business runs.
Clean, reliable endpoints with clear documentation and no vendor lock-in. Startup or enterprise: you ship automations and integrations in days, not months.
-
Modular by design
Connect just what you need, invoices, customers, contracts, and more, without locking into a rigid system.
-
Build faster, scale smarter
A fast, stable API lets your team build automations and integrations in days, not months.
-
Integrates with your stack
Plug Enlivy into the HR, finance, and CRM systems you already run.
-
No vendor lock-in
Clean, documented endpoints you stay in control of, with total flexibility.
The Challenges Holding Your Business Back
Brittle integrations and repetitive manual work, the things that keep a business from scaling.
-
Systems that will not talk
Without Enlivy: Connecting your HR, finance, and CRM is a frustrating tangle of manual work and unreliable integrations.
With Enlivy: Every record is reachable through one clean API, so your systems finally connect and stay in sync.
-
Manual work that will not scale
Without Enlivy: Repetitive manual tasks drain productivity, increase errors, and block your ability to scale with confidence.
With Enlivy: Script the repetitive work, invoicing and data syncing, and let automation carry it as you grow.
API Reference
Invoice Management from the API
Integrate directly with Enlivy through our public API for programmatic access to your account data: invoices, receipts, transactions, products, users, contracts, and more.
The examples below cover the most common invoicing operations: listing invoices, retrieving a single invoice, creating a new one, and updating existing invoice data.
Display a list of recent invoices in your dashboard. Query invoices for your organization with pagination, filters, and metadata. You can also expand invoice details such as line items and taxes.
/organizations/<organization_id>/invoices curl -G 'https://api.enlivy.com/organizations/<organization_id>/invoices' \
--data-urlencode 'page=1' \
--data-urlencode 'limit=20' \
--data-urlencode 'include=invoice_prefix,tag_ids,line_items,taxes' \
--data-urlencode 'include_meta=navigation' \
-H "Authorization: Bearer $ENLIVY_API_TOKEN" \
-H 'Accept: application/json' const baseUrl = "https://api.enlivy.com" ;
const token = process.env.ENLIVY_API_TOKEN;
const organizationId = "<organization_id>";
const query = new URLSearchParams({
page: "1",
limit: "20",
include: "invoice_prefix,tag_ids,line_items,taxes",
include_meta: "navigation",
});
const result = await fetch(`${baseUrl}/organizations/${organizationId}/invoices?${query}` , {
method: "GET",
headers: {
Authorization: `Bearer ${token}`,
Accept: "application/json" ,
},
}).then((response) => response.json());Retrieves detailed information about a specific invoice within your organization. Use this to access invoice data such as totals, line items, status, taxes, tags, and more.
/organizations/<organization_id>/invoices/<invoice_id> curl -G 'https://api.enlivy.com/organizations/<organization_id>/invoices/<invoice_id>' \
--data-urlencode 'include=invoice_prefix,tag_ids,line_items,taxes' \
-H "Authorization: Bearer $ENLIVY_API_TOKEN" \
-H 'Accept: application/json' const baseUrl = "https://api.enlivy.com" ;
const token = process.env.ENLIVY_API_TOKEN;
const organizationId = "<organization_id>";
const invoiceId = "<invoice_id>";
const query = new URLSearchParams({
include: "invoice_prefix,tag_ids,line_items,taxes",
});
const result = await fetch(`${baseUrl}/organizations/${organizationId}/invoices/${invoiceId}?${query}` , {
method: "GET",
headers: {
Authorization: `Bearer ${token}`,
Accept: "application/json" ,
},
}).then((response) => response.json());Creates a new invoice under your organization. You can include detailed data such as sender and receiver users, invoice prefix, currency, line items, and more.
/organizations/<organization_id>/invoices # source (internal), direction (outbound) and type (standard) take their defaults when left out.
curl -X POST 'https://api.enlivy.com/organizations/<organization_id>/invoices' \
-H "Authorization: Bearer $ENLIVY_API_TOKEN" \
-H 'Accept: application/json' \
-H 'Content-Type: application/json' \
-d '{
"organization_receiver_user_id": "<org_user_receiver_id>",
"status": "draft",
"currency": "EUR",
"payment_method": "bank_transfer",
"delivery_method": "email",
"line_items": [
{
"name_lang_map": {
"en": "Web Development Services"
},
"quantity": 10,
"price": 75,
"type": "service",
"organization_tax_class_id": "<tax_class_id>"
}
]
}'const baseUrl = "https://api.enlivy.com" ;
const token = process.env.ENLIVY_API_TOKEN;
const organizationId = "<organization_id>";
// source (internal), direction (outbound) and type (standard) take their defaults when left out.
const result = await fetch(`${baseUrl}/organizations/${organizationId}/invoices` , {
method: "POST",
headers: {
Authorization: `Bearer ${token}`,
Accept: "application/json" ,
"Content-Type": "application/json" ,
},
body: JSON.stringify({
// the customer the invoice is issued to
organization_receiver_user_id: "<org_user_receiver_id>",
// draft | pending | sent_email | paid | ...
status: "draft",
currency: "EUR",
// bank_transfer | card_payment | cash | ...
payment_method: "bank_transfer",
delivery_method: "email",
line_items: [
// or send organization_product_id to bill one of your products
{
name_lang_map: { en: "Web Development Services" },
quantity: 10,
price: 75,
type: "service",
organization_tax_class_id: "<tax_class_id>",
},
],
}),
}).then((response) => response.json());Update an existing invoice’s content such as its status (e.g. issued, paid, canceled), payment method, due date, number, tags, notes, and product lines.
/organizations/<organization_id>/invoices/<invoice_id> curl -X PUT 'https://api.enlivy.com/organizations/<organization_id>/invoices/<invoice_id>' \
-H "Authorization: Bearer $ENLIVY_API_TOKEN" \
-H 'Accept: application/json' \
-H 'Content-Type: application/json' \
-d '{
"status": "paid",
"payment_method": "bank_transfer",
"due_at": "2026-07-31T09:00:00Z"
}'const baseUrl = "https://api.enlivy.com" ;
const token = process.env.ENLIVY_API_TOKEN;
const organizationId = "<organization_id>";
const invoiceId = "<invoice_id>";
const result = await fetch(`${baseUrl}/organizations/${organizationId}/invoices/${invoiceId}` , {
method: "PUT",
headers: {
Authorization: `Bearer ${token}`,
Accept: "application/json" ,
"Content-Type": "application/json" ,
},
body: JSON.stringify({
// partial update, only the fields you send are touched
status: "paid",
payment_method: "bank_transfer",
due_at: "2026-07-31T09:00:00Z",
}),
}).then((response) => response.json());API Reference
Billing Schedules from the API
Automate recurring revenue end-to-end. An app-managed billing schedule is run entirely by Enlivy: it generates invoices on the cadence you define, charges the saved payment method, and moves through its own lifecycle: pending, active, paused, cancelling, completed. You define the composition once; the engine bills it on schedule.
This reference covers app-managed schedules only. Enlivy also supports Stripe-hosted schedules, where Stripe owns the subscription and runs the billing. Those are managed on Stripe’s side, so their creation and modification aren’t covered here. You can compose an app-managed schedule two ways: from raw phases (your own recurring line items and cadence) or from a subscription billing package (a reusable template). The examples below cover both, plus listing, editing, reconfiguring, and cancelling.
Query your schedules with pagination, filtering, and full-text search. Filter by status, direction (outbound / inbound), sender, receiver, contract, bank account, or any of the date ranges (starts_at, ends_at, created_at, updated_at). Expand the phases and generated payments inline.
/organizations/<organization_id>/billing-schedules # status: pending | active | payment_method_required | paused | cancelling | completed | cancelled
curl -G 'https://api.enlivy.com/organizations/<organization_id>/billing-schedules' \
--data-urlencode 'page=1' \
--data-urlencode 'limit=20' \
--data-urlencode 'status=active' \
--data-urlencode 'direction=outbound' \
--data-urlencode 'include=receiver_user,phases,payments' \
--data-urlencode 'include_meta=navigation' \
-H "Authorization: Bearer $ENLIVY_API_TOKEN" \
-H 'Accept: application/json' const baseUrl = "https://api.enlivy.com" ;
const token = process.env.ENLIVY_API_TOKEN;
const organizationId = "<organization_id>";
// status: pending | active | payment_method_required | paused | cancelling | completed | cancelled
const query = new URLSearchParams({
page: "1",
limit: "20",
status: "active",
direction: "outbound",
include: "receiver_user,phases,payments",
include_meta: "navigation",
});
const result = await fetch(`${baseUrl}/organizations/${organizationId}/billing-schedules?${query}` , {
method: "GET",
headers: {
Authorization: `Bearer ${token}`,
Accept: "application/json" ,
},
}).then((response) => response.json());Retrieve a single schedule with everything attached: its phases and line items, the payments generated so far, the counterparties, and the linked package or contract. Read next_payment_create_at to see when the engine bills next, and total / paid_total for progress.
/organizations/<organization_id>/billing-schedules/<billing_schedule_id> # includes: sender_user, receiver_user, contract, billing_package,
# subscription_term, phases, payments, deleted_by_user
curl -G 'https://api.enlivy.com/organizations/<organization_id>/billing-schedules/<billing_schedule_id>' \
--data-urlencode 'include=phases,payments,receiver_user,billing_package' \
-H "Authorization: Bearer $ENLIVY_API_TOKEN" \
-H 'Accept: application/json' const baseUrl = "https://api.enlivy.com" ;
const token = process.env.ENLIVY_API_TOKEN;
const organizationId = "<organization_id>";
const billingScheduleId = "<billing_schedule_id>";
// includes: sender_user, receiver_user, contract, billing_package,
// subscription_term, phases, payments, deleted_by_user
const query = new URLSearchParams({
include: "phases,payments,receiver_user,billing_package",
});
const result = await fetch(`${baseUrl}/organizations/${organizationId}/billing-schedules/${billingScheduleId}?${query}` , {
method: "GET",
headers: {
Authorization: `Bearer ${token}`,
Accept: "application/json" ,
},
}).then((response) => response.json());The most direct way to bill: define one or more phases, each with its own cadence and line items. The engine issues an invoice per phase occurrence and charges it. Phases are exclusive to app-managed schedules.
/organizations/<organization_id>/billing-schedules curl -X POST 'https://api.enlivy.com/organizations/<organization_id>/billing-schedules' \
-H "Authorization: Bearer $ENLIVY_API_TOKEN" \
-H 'Accept: application/json' \
-H 'Content-Type: application/json' \
-d '{
"management_type": "app_managed",
"direction": "outbound",
"status": "active",
"organization_sender_user_id": "<org_user_sender_id>",
"organization_receiver_user_id": "<org_user_receiver_id>",
"currency": "EUR",
"payment_method": "stripe_card_payment",
"organization_user_payment_method_id": "<payment_method_id>",
"name_lang_map": {
"en": "Monthly Retainer"
},
"note_lang_map": {
"en": "Includes support and hosting."
},
"is_email_notifications_active": true,
"email_notifications_to": "billing@acme.example",
"customer_can_reconfigure": false,
"customer_can_cancel": true,
"customer_can_pause": false,
"phases": [
{
"frequency": "monthly",
"execute_after": "2026-08-01",
"max_occurrences": 12,
"due_date_type": "custom_days",
"due_date_days": 14,
"order": 0,
"line_items": [
{
"name_lang_map": {
"en": "Retainer"
},
"quantity": 1,
"price": 500,
"organization_tax_class_id": "<tax_class_id>"
},
{
"organization_product_id": "<product_id>",
"quantity": 2,
"price": 49.9
}
]
}
]
}'const baseUrl = "https://api.enlivy.com" ;
const token = process.env.ENLIVY_API_TOKEN;
const organizationId = "<organization_id>";
const result = await fetch(`${baseUrl}/organizations/${organizationId}/billing-schedules` , {
method: "POST",
headers: {
Authorization: `Bearer ${token}`,
Accept: "application/json" ,
"Content-Type": "application/json" ,
},
body: JSON.stringify({
// this reference only covers app_managed
management_type: "app_managed",
// outbound = you bill a customer; inbound = you are billed
direction: "outbound",
// start billing right away
status: "active",
// the biller (you)
organization_sender_user_id: "<org_user_sender_id>",
// the customer (required for outbound)
organization_receiver_user_id: "<org_user_receiver_id>",
currency: "EUR",
// bank_transfer | card_payment | stripe_card_payment | paypal | cash | paid_by_personal_funds
payment_method: "stripe_card_payment",
// saved card to auto-charge (must belong to the receiver)
organization_user_payment_method_id: "<payment_method_id>",
name_lang_map: {
// per-locale maps, not plain strings
en: "Monthly Retainer",
},
note_lang_map: { en: "Includes support and hosting." },
is_email_notifications_active: true,
email_notifications_to: "billing@acme.example",
// customer self-service toggles (what the recipient may do from the portal)
customer_can_reconfigure: false,
customer_can_cancel: true,
customer_can_pause: false,
// totals + dates (total, paid_total, starts_at, ends_at, next_payment_create_at)
// are computed by the engine, do NOT send them.
// phases are valid ONLY for app_managed (stripe_hosted rejects them). Max 20.
phases: [
{
// weekly | biweekly | monthly | yearly
frequency: "monthly",
// first run date for this phase
execute_after: "2026-08-01",
// stop after N invoices (omit for open-ended)
max_occurrences: 12,
// immediate | end_of_month | custom_days
due_date_type: "custom_days",
// required only when due_date_type = custom_days
due_date_days: 14,
order: 0,
line_items: [
{
name_lang_map: {
// required unless organization_product_id is set
en: "Retainer",
},
quantity: 1,
// money: signed decimal in MAJOR units (not cents)
price: 500,
organization_tax_class_id: "<tax_class_id>",
},
{
// pulls name / tax defaults from the product
organization_product_id: "<product_id>",
quantity: 2,
price: 49.9,
},
],
},
],
}),
}).then((response) => response.json());Instead of hand-building phases, materialize the schedule from a reusable subscription billing package. The package owns the composition, you just pick the cadence variant and which group items to include.
/organizations/<organization_id>/billing-schedules/from-billing-package # The package supplies the phases and the line items, so none are sent here.
curl -X POST 'https://api.enlivy.com/organizations/<organization_id>/billing-schedules/from-billing-package' \
-H "Authorization: Bearer $ENLIVY_API_TOKEN" \
-H 'Accept: application/json' \
-H 'Content-Type: application/json' \
-d '{
"organization_billing_package_id": "<billing_package_id>",
"organization_billing_package_subscription_term_id": "<subscription_term_id>",
"selected_group_items": [
{
"id": "<group_item_id>",
"quantity": 1
},
{
"id": "<other_group_item_id>",
"quantity": 3
}
],
"organization_sender_user_id": "<org_user_sender_id>",
"organization_receiver_user_id": "<org_user_receiver_id>",
"status": "active",
"payment_method": "bank_transfer",
"currency": "EUR",
"start_at": "2026-11-01",
"is_email_notifications_active": true
}'const baseUrl = "https://api.enlivy.com" ;
const token = process.env.ENLIVY_API_TOKEN;
const organizationId = "<organization_id>";
// The package supplies the phases and the line items, so none are sent here.
const result = await fetch(`${baseUrl}/organizations/${organizationId}/billing-schedules/from-billing-package` , {
method: "POST",
headers: {
Authorization: `Bearer ${token}`,
Accept: "application/json" ,
"Content-Type": "application/json" ,
},
body: JSON.stringify({
// attach a SUBSCRIPTION package, it defines the phases & line items
organization_billing_package_id: "<billing_package_id>",
// optional: pick the cadence variant; omit to use the package default
organization_billing_package_subscription_term_id: "<subscription_term_id>",
// optional: choose which package group items (and quantities) to include
selected_group_items: [
{ id: "<group_item_id>", quantity: 1 },
{ id: "<other_group_item_id>", quantity: 3 },
],
organization_sender_user_id: "<org_user_sender_id>",
organization_receiver_user_id: "<org_user_receiver_id>",
// pending | active
status: "active",
payment_method: "bank_transfer",
currency: "EUR",
start_at: "2026-11-01",
is_email_notifications_active: true,
}),
}).then((response) => response.json());Update metadata (name, notes, notification settings, self-service toggles) and drive the lifecycle by moving status: pause an active schedule, resume a paused one, or set it to cancel. Send only what you want to change.
/organizations/<organization_id>/billing-schedules/<billing_schedule_id> # to change WHAT gets billed on a package-managed schedule,
# use /reconfigure below rather than editing here.
curl -X PUT 'https://api.enlivy.com/organizations/<organization_id>/billing-schedules/<billing_schedule_id>' \
-H "Authorization: Bearer $ENLIVY_API_TOKEN" \
-H 'Accept: application/json' \
-H 'Content-Type: application/json' \
-d '{
"status": "paused",
"note_lang_map": {
"en": "Paused at customer request."
},
"is_email_notifications_active": false,
"customer_can_cancel": true
}'const baseUrl = "https://api.enlivy.com" ;
const token = process.env.ENLIVY_API_TOKEN;
const organizationId = "<organization_id>";
const billingScheduleId = "<billing_schedule_id>";
// to change WHAT gets billed on a package-managed schedule,
// use /reconfigure below rather than editing here.
const result = await fetch(`${baseUrl}/organizations/${organizationId}/billing-schedules/${billingScheduleId}` , {
method: "PUT",
headers: {
Authorization: `Bearer ${token}`,
Accept: "application/json" ,
"Content-Type": "application/json" ,
},
body: JSON.stringify({
// lifecycle: active <-> paused; set "cancelling" to stop at the end of the cycle
status: "paused",
note_lang_map: { en: "Paused at customer request." },
is_email_notifications_active: false,
customer_can_cancel: true,
}),
}).then((response) => response.json());Change what a package-managed schedule bills: swap the package, change the term, adjust selected items and quantities, override prices, or add one-off custom line items. Always preview first: the preview endpoint returns the resulting composition and any proration before a cent is charged.
/organizations/<organization_id>/billing-schedules/<billing_schedule_id>/preview-reconfigure +1# 1. Preview, no changes are applied; returns the new composition + proration
curl -X POST 'https://api.enlivy.com/organizations/<organization_id>/billing-schedules/<billing_schedule_id>/preview-reconfigure' \
-H "Authorization: Bearer $ENLIVY_API_TOKEN" \
-H 'Accept: application/json' \
-H 'Content-Type: application/json' \
-d '{
"organization_billing_package_id": "<billing_package_id>",
"organization_billing_package_subscription_term_id": "<subscription_term_id>",
"selected_group_items": [
{
"id": "<group_item_id>",
"quantity": 2,
"price_override": 39,
"pricing_currency": "EUR"
}
],
"custom_line_items": [
{
"name_lang_map": {
"en": "One-off setup"
},
"quantity": 1,
"price": 150
}
],
"proration_mode": "prorate_next_invoice"
}'
# 2. Apply
curl -X PUT 'https://api.enlivy.com/organizations/<organization_id>/billing-schedules/<billing_schedule_id>/reconfigure' \
-H "Authorization: Bearer $ENLIVY_API_TOKEN" \
-H 'Accept: application/json' \
-H 'Content-Type: application/json' \
-d '{
"organization_billing_package_id": "<billing_package_id>",
"organization_billing_package_subscription_term_id": "<subscription_term_id>",
"selected_group_items": [
{
"id": "<group_item_id>",
"quantity": 2,
"price_override": 39,
"pricing_currency": "EUR"
}
],
"custom_line_items": [
{
"name_lang_map": {
"en": "One-off setup"
},
"quantity": 1,
"price": 150
}
],
"proration_mode": "prorate_next_invoice"
}'const baseUrl = "https://api.enlivy.com" ;
const token = process.env.ENLIVY_API_TOKEN;
const organizationId = "<organization_id>";
const billingScheduleId = "<billing_schedule_id>";
const headers = {
Authorization: `Bearer ${token}`,
Accept: "application/json" ,
"Content-Type": "application/json" ,
};
// 1. Preview, no changes are applied; returns the new composition + proration
const result1 = await fetch(`${baseUrl}/organizations/${organizationId}/billing-schedules/${billingScheduleId}/preview-reconfigure` , {
method: "POST",
headers,
body: JSON.stringify({
organization_billing_package_id: "<billing_package_id>",
organization_billing_package_subscription_term_id: "<subscription_term_id>",
selected_group_items: [
// price_override + pricing_currency let you deviate from the package price
{
id: "<group_item_id>",
quantity: 2,
price_override: 39,
pricing_currency: "EUR",
},
],
custom_line_items: [{ name_lang_map: { en: "One-off setup" }, quantity: 1, price: 150 }],
// how the switch is billed: none | prorate_immediately | prorate_next_invoice
// (prorate_immediately requires an active schedule)
proration_mode: "prorate_next_invoice",
}),
}).then((response) => response.json());
// 2. Apply
const result2 = await fetch(`${baseUrl}/organizations/${organizationId}/billing-schedules/${billingScheduleId}/reconfigure` , {
method: "PUT",
headers,
body: JSON.stringify({
organization_billing_package_id: "<billing_package_id>",
organization_billing_package_subscription_term_id: "<subscription_term_id>",
selected_group_items: [
// price_override + pricing_currency let you deviate from the package price
{
id: "<group_item_id>",
quantity: 2,
price_override: 39,
pricing_currency: "EUR",
},
],
custom_line_items: [{ name_lang_map: { en: "One-off setup" }, quantity: 1, price: 150 }],
// how the switch is billed: none | prorate_immediately | prorate_next_invoice
// (prorate_immediately requires an active schedule)
proration_mode: "prorate_next_invoice",
}),
}).then((response) => response.json());Read aggregate recurring-revenue analytics across your schedules: a point-in-time summary (with forward projection) or a history_monthly breakdown, to power MRR dashboards without pulling every schedule yourself.
/organizations/<organization_id>/billing-schedules/analytics/summary # analyticsType: summary | history_monthly
curl -G 'https://api.enlivy.com/organizations/<organization_id>/billing-schedules/analytics/summary' \
--data-urlencode 'start_date=2026-01-01T00:00:00Z' \
--data-urlencode 'end_date=2026-12-31T23:59:59Z' \
--data-urlencode 'direction=outbound' \
--data-urlencode 'convert_to_currency=EUR' \
-H "Authorization: Bearer $ENLIVY_API_TOKEN" \
-H 'Accept: application/json' const baseUrl = "https://api.enlivy.com" ;
const token = process.env.ENLIVY_API_TOKEN;
const organizationId = "<organization_id>";
const analyticsType = "summary";
// analyticsType: summary | history_monthly
const query = new URLSearchParams({
start_date: "2026-01-01T00:00:00Z",
end_date: "2026-12-31T23:59:59Z",
direction: "outbound",
convert_to_currency: "EUR",
});
const result = await fetch(`${baseUrl}/organizations/${organizationId}/billing-schedules/analytics/${analyticsType}?${query}` , {
method: "GET",
headers: {
Authorization: `Bearer ${token}`,
Accept: "application/json" ,
},
}).then((response) => response.json());Soft-delete a schedule. Prefer setting status to cancelling (stops future billing at the end of the cycle) when you want to keep the record; delete when you want it gone. It can be restored later.
/organizations/<organization_id>/billing-schedules/<billing_schedule_id> # soft delete, restore via POST /billing-schedules/restore/{scheduleId}
curl -X DELETE 'https://api.enlivy.com/organizations/<organization_id>/billing-schedules/<billing_schedule_id>' \
-H "Authorization: Bearer $ENLIVY_API_TOKEN" \
-H 'Accept: application/json' const baseUrl = "https://api.enlivy.com" ;
const token = process.env.ENLIVY_API_TOKEN;
const organizationId = "<organization_id>";
const billingScheduleId = "<billing_schedule_id>";
// soft delete, restore via POST /billing-schedules/restore/{scheduleId}
const result = await fetch(`${baseUrl}/organizations/${organizationId}/billing-schedules/${billingScheduleId}` , {
method: "DELETE",
headers: {
Authorization: `Bearer ${token}`,
Accept: "application/json" ,
},
}).then((response) => response.json());API Reference
Network Exchanges from the API
Send e-invoices for your Romanian clients to ANAF e-Factura, and pull the ones addressed to you, programmatically. A network exchange is a single transmission of an invoice to (or from) an institution; the supported institution is ANAF. Enlivy builds the compliant UBL document, submits it, and tracks the response through its lifecycle so you know whether a document was accepted, rejected, or still processing.
Exchanges are outbound (you submit an invoice you issued) or inbound (you pull documents addressed to you). This reference starts with the key action, pushing an invoice onto the network, then covers listing, inspecting, pulling inbound documents, and downloading the signed files. Every exchange carries a status you can poll.
Submit an issued invoice to a network institution. The invoice is the payload; there is no request body. Enlivy generates the compliant UBL, transmits it, and returns the resulting network-exchange record with its initial status. Which institutions an invoice can go to is exposed on the invoice itself.
/organizations/<organization_id>/invoices/<invoice_id> +1# 1. Discover eligible institutions for this invoice.
# invoice.peppol_exchange_push_options lists the institution ids you may push to;
# invoice.peppol_exchanges_pushed lists the ones already sent.
curl 'https://api.enlivy.com/organizations/<organization_id>/invoices/<invoice_id>' \
-H "Authorization: Bearer $ENLIVY_API_TOKEN" \
-H 'Accept: application/json'
# 2. Push to a specific institution. The id is a string, e.g. "anaf" (Romania / ANAF).
# no request body, the invoice is the document
curl -X POST 'https://api.enlivy.com/organizations/<organization_id>/invoices/<invoice_id>/peppol/anaf' \
-H "Authorization: Bearer $ENLIVY_API_TOKEN" \
-H 'Accept: application/json'
# The response is the network-exchange record. Poll its `status`:
# pending | processing | success | success_in_exchange_queue | success_pending_archive
# | change_required | rejected | rejected_at_exchange | failed | failed_credentials_expired
# A transport/connectivity failure to the institution returns HTTP 503. const baseUrl = "https://api.enlivy.com" ;
const token = process.env.ENLIVY_API_TOKEN;
const organizationId = "<organization_id>";
const invoiceId = "<invoice_id>";
const institution = "anaf";
const headers = {
Authorization: `Bearer ${token}`,
Accept: "application/json" ,
};
// 1. Discover eligible institutions for this invoice.
// invoice.peppol_exchange_push_options lists the institution ids you may push to;
// invoice.peppol_exchanges_pushed lists the ones already sent.
const invoice = await fetch(`${baseUrl}/organizations/${organizationId}/invoices/${invoiceId}` , {
method: "GET",
headers,
}).then((response) => response.json());
// 2. Push to a specific institution. The id is a string, e.g. "anaf" (Romania / ANAF).
// no request body, the invoice is the document
const exchange = await fetch(`${baseUrl}/organizations/${organizationId}/invoices/${invoiceId}/peppol/${institution}` , {
method: "POST",
headers,
}).then((response) => response.json());
// The response is the network-exchange record. Poll its `status`:
// pending | processing | success | success_in_exchange_queue | success_pending_archive
// | change_required | rejected | rejected_at_exchange | failed | failed_credentials_expired
// A transport/connectivity failure to the institution returns HTTP 503. List every exchange with pagination and filtering: scope to one invoice, filter by exchange status or by the linked invoice’s state, and bound by date. This is how you build a compliance dashboard of what’s been sent and where each document stands.
/organizations/<organization_id>/invoices/network-exchanges # filters: status, organization_invoice_id, invoice_state (the linked invoice's state),
# created_at_from/to, updated_at_from/to, ids
curl -G 'https://api.enlivy.com/organizations/<organization_id>/invoices/network-exchanges' \
--data-urlencode 'page=1' \
--data-urlencode 'limit=20' \
--data-urlencode 'status=success' \
--data-urlencode 'include=invoice' \
-H "Authorization: Bearer $ENLIVY_API_TOKEN" \
-H 'Accept: application/json' const baseUrl = "https://api.enlivy.com" ;
const token = process.env.ENLIVY_API_TOKEN;
const organizationId = "<organization_id>";
// filters: status, organization_invoice_id, invoice_state (the linked invoice's state),
// created_at_from/to, updated_at_from/to, ids
const query = new URLSearchParams({
page: "1",
limit: "20",
status: "success",
include: "invoice",
});
const result = await fetch(`${baseUrl}/organizations/${organizationId}/invoices/network-exchanges?${query}` , {
method: "GET",
headers: {
Authorization: `Bearer ${token}`,
Accept: "application/json" ,
},
}).then((response) => response.json());Retrieve a single exchange: its status, the institution’s exchange and message identifiers, the raw institution response_json, and the names of the generated files. Include parsed_data to get the parsed contents of the exchanged document, or invoice for the source invoice.
/organizations/<organization_id>/invoices/network-exchanges/<peppol_network_exchange_id> # includes: organization, invoice, parsed_data (the parsed UBL as structured data), tag_ids
curl -G 'https://api.enlivy.com/organizations/<organization_id>/invoices/network-exchanges/<peppol_network_exchange_id>' \
--data-urlencode 'include=parsed_data,invoice' \
-H "Authorization: Bearer $ENLIVY_API_TOKEN" \
-H 'Accept: application/json' const baseUrl = "https://api.enlivy.com" ;
const token = process.env.ENLIVY_API_TOKEN;
const organizationId = "<organization_id>";
const peppolNetworkExchangeId = "<peppol_network_exchange_id>";
// includes: organization, invoice, parsed_data (the parsed UBL as structured data), tag_ids
const query = new URLSearchParams({
include: "parsed_data,invoice",
});
const result = await fetch(`${baseUrl}/organizations/${organizationId}/invoices/network-exchanges/${peppolNetworkExchangeId}?${query}` , {
method: "GET",
headers: {
Authorization: `Bearer ${token}`,
Accept: "application/json" ,
},
}).then((response) => response.json());Fetch documents that were sent to you over an institution’s network, supplier invoices delivered through ANAF, for example. Enlivy retrieves and ingests them as inbound exchanges. Bound the sync with date_from.
/organizations/<organization_id>/invoices/network-exchanges/anaf/pull curl -G 'https://api.enlivy.com/organizations/<organization_id>/invoices/network-exchanges/anaf/pull' \
--data-urlencode 'date_from=2026-07-01' \
-H "Authorization: Bearer $ENLIVY_API_TOKEN" \
-H 'Accept: application/json' const baseUrl = "https://api.enlivy.com" ;
const token = process.env.ENLIVY_API_TOKEN;
const organizationId = "<organization_id>";
const institutionId = "anaf";
const query = new URLSearchParams({
date_from: "2026-07-01",
});
const result = await fetch(`${baseUrl}/organizations/${organizationId}/invoices/network-exchanges/${institutionId}/pull?${query}` , {
method: "GET",
headers: {
Authorization: `Bearer ${token}`,
Accept: "application/json" ,
},
}).then((response) => response.json());Pull the artifacts of an exchange: the compliant UBL/XML document, its digital signature, and the human-readable PDF. Each returns a file stream.
/organizations/<organization_id>/invoices/network-exchanges/<peppol_network_exchange_id>/download +2# the signed UBL / XML document
curl 'https://api.enlivy.com/organizations/<organization_id>/invoices/network-exchanges/<peppol_network_exchange_id>/download' \
-H "Authorization: Bearer $ENLIVY_API_TOKEN" \
--remote-name --remote-header-name
# the detached digital signature
curl 'https://api.enlivy.com/organizations/<organization_id>/invoices/network-exchanges/<peppol_network_exchange_id>/download-signature' \
-H "Authorization: Bearer $ENLIVY_API_TOKEN" \
--remote-name --remote-header-name
# the human-readable PDF rendering
curl 'https://api.enlivy.com/organizations/<organization_id>/invoices/network-exchanges/<peppol_network_exchange_id>/download-pdf' \
-H "Authorization: Bearer $ENLIVY_API_TOKEN" \
--remote-name --remote-header-nameconst baseUrl = "https://api.enlivy.com" ;
const token = process.env.ENLIVY_API_TOKEN;
const organizationId = "<organization_id>";
const peppolNetworkExchangeId = "<peppol_network_exchange_id>";
const headers = {
Authorization: `Bearer ${token}`,
};
// the signed UBL / XML document
const xml = await fetch(`${baseUrl}/organizations/${organizationId}/invoices/network-exchanges/${peppolNetworkExchangeId}/download` , {
method: "GET",
headers,
}).then((response) => response.blob());
// the detached digital signature
const signature = await fetch(`${baseUrl}/organizations/${organizationId}/invoices/network-exchanges/${peppolNetworkExchangeId}/download-signature` , {
method: "GET",
headers,
}).then((response) => response.blob());
// the human-readable PDF rendering
const pdf = await fetch(`${baseUrl}/organizations/${organizationId}/invoices/network-exchanges/${peppolNetworkExchangeId}/download-pdf` , {
method: "GET",
headers,
}).then((response) => response.blob());Retrieve the institution-side status and validation details for an exchange, useful when a document comes back as change_required or rejected and you need the reasons to correct and resubmit.
/organizations/<organization_id>/invoices/network-exchanges/<peppol_network_exchange_id>/information curl 'https://api.enlivy.com/organizations/<organization_id>/invoices/network-exchanges/<peppol_network_exchange_id>/information' \
-H "Authorization: Bearer $ENLIVY_API_TOKEN" \
-H 'Accept: application/json' const baseUrl = "https://api.enlivy.com" ;
const token = process.env.ENLIVY_API_TOKEN;
const organizationId = "<organization_id>";
const peppolNetworkExchangeId = "<peppol_network_exchange_id>";
const result = await fetch(`${baseUrl}/organizations/${organizationId}/invoices/network-exchanges/${peppolNetworkExchangeId}/information` , {
method: "GET",
headers: {
Authorization: `Bearer ${token}`,
Accept: "application/json" ,
},
}).then((response) => response.json());API Reference
Bank Transactions from the API
Bring every movement through your accounts into one ledger, then make sense of it. Bank transactions land in Enlivy from bank sync, Stripe payouts, or manual entry, and the API lets you classify them by cost type and reconcile them against the invoices, receipts, and payslips they settle. Each transaction tracks its own state, from backlog to fully connected.
Two resources work together: cost types (your reusable classification vocabulary like “Payroll”, “Client Payment”, “Software”, each declaring what it may be linked to) and bank transactions, the movements themselves. The examples below cover both, plus listing, filtering, reconciling, and analytics.
List the cost types you classify transactions with. Each carries a localized label, whether a link to a business entity is required, and which entity types it may connect to.
/organizations/<organization_id>/bank-transaction-cost-types curl -G 'https://api.enlivy.com/organizations/<organization_id>/bank-transaction-cost-types' \
--data-urlencode 'page=1' \
--data-urlencode 'limit=50' \
-H "Authorization: Bearer $ENLIVY_API_TOKEN" \
-H 'Accept: application/json' const baseUrl = "https://api.enlivy.com" ;
const token = process.env.ENLIVY_API_TOKEN;
const organizationId = "<organization_id>";
const query = new URLSearchParams({
page: "1",
limit: "50",
});
const result = await fetch(`${baseUrl}/organizations/${organizationId}/bank-transaction-cost-types?${query}` , {
method: "GET",
headers: {
Authorization: `Bearer ${token}`,
Accept: "application/json" ,
},
}).then((response) => response.json());Define a classification category. Set whether transactions of this type must be reconciled, and, if so, which entity types they may be connected to. Cost types can be nested under a parent for a hierarchy.
/organizations/<organization_id>/bank-transaction-cost-types curl -X POST 'https://api.enlivy.com/organizations/<organization_id>/bank-transaction-cost-types' \
-H "Authorization: Bearer $ENLIVY_API_TOKEN" \
-H 'Accept: application/json' \
-H 'Content-Type: application/json' \
-d '{
"title_lang_map": {
"en": "Client Payment",
"ro": "Încasare client"
},
"connection_required": true,
"connection_types": [
"invoice",
"receipt"
],
"organization_bank_transaction_cost_type_id": null
}'const baseUrl = "https://api.enlivy.com" ;
const token = process.env.ENLIVY_API_TOKEN;
const organizationId = "<organization_id>";
const result = await fetch(`${baseUrl}/organizations/${organizationId}/bank-transaction-cost-types` , {
method: "POST",
headers: {
Authorization: `Bearer ${token}`,
Accept: "application/json" ,
"Content-Type": "application/json" ,
},
body: JSON.stringify({
title_lang_map: {
en: "Client Payment",
// per-locale map
ro: "Încasare client",
},
// does a transaction of this type have to be linked to a business entity?
connection_required: true,
// which entity types it may be connected to (required when connection_required = true)
// options: invoice | receipt | payslip | bank_transaction | user
connection_types: [
"invoice",
"receipt",
],
// optional: parent cost type for a nested / hierarchical category
organization_bank_transaction_cost_type_id: null,
}),
}).then((response) => response.json());Query transactions with pagination, search, and filters. Filter by classification state, direction, or by a specific connected entity, and bound by date. Expand the assigned cost type and the connected entities inline.
/organizations/<organization_id>/bank-transactions # state: backlog | classified | connected | connected_partially | danger | trashed
# find transactions connected to a specific entity, send BOTH params together:
# `&connection_entity_type=invoice&connection_entity_id=invoice_id` +
# free-text search with q=... (min 3 chars) instead of ids
curl -G 'https://api.enlivy.com/organizations/<organization_id>/bank-transactions' \
--data-urlencode 'page=1' \
--data-urlencode 'limit=20' \
--data-urlencode 'state=backlog' \
--data-urlencode 'direction=inbound' \
--data-urlencode 'include=cost_type,connection_entities,bank_account' \
-H "Authorization: Bearer $ENLIVY_API_TOKEN" \
-H 'Accept: application/json' const baseUrl = "https://api.enlivy.com" ;
const token = process.env.ENLIVY_API_TOKEN;
const organizationId = "<organization_id>";
// state: backlog | classified | connected | connected_partially | danger | trashed
// find transactions connected to a specific entity, send BOTH params together:
// `&connection_entity_type=invoice&connection_entity_id=invoice_id` +
// free-text search with q=... (min 3 chars) instead of ids
const query = new URLSearchParams({
page: "1",
limit: "20",
state: "backlog",
direction: "inbound",
include: "cost_type,connection_entities,bank_account",
});
const result = await fetch(`${baseUrl}/organizations/${organizationId}/bank-transactions?${query}` , {
method: "GET",
headers: {
Authorization: `Bearer ${token}`,
Accept: "application/json" ,
},
}).then((response) => response.json());Retrieve a single transaction: its amount and currency, direction, the assigned cost type, the entities it’s connected to, and any Stripe payout / reporting metadata for synced transactions.
/organizations/<organization_id>/bank-transactions/<bank_transaction_id> # includes: organization, bank_account, cost_type, connection_entities, deleted_by_user, tag_ids
curl -G 'https://api.enlivy.com/organizations/<organization_id>/bank-transactions/<bank_transaction_id>' \
--data-urlencode 'include=cost_type,connection_entities,bank_account' \
-H "Authorization: Bearer $ENLIVY_API_TOKEN" \
-H 'Accept: application/json' const baseUrl = "https://api.enlivy.com" ;
const token = process.env.ENLIVY_API_TOKEN;
const organizationId = "<organization_id>";
const bankTransactionId = "<bank_transaction_id>";
// includes: organization, bank_account, cost_type, connection_entities, deleted_by_user, tag_ids
const query = new URLSearchParams({
include: "cost_type,connection_entities,bank_account",
});
const result = await fetch(`${baseUrl}/organizations/${organizationId}/bank-transactions/${bankTransactionId}?${query}` , {
method: "GET",
headers: {
Authorization: `Bearer ${token}`,
Accept: "application/json" ,
},
}).then((response) => response.json());Record a transaction manually (most transactions arrive automatically via bank sync or Stripe). You can classify it in the same call by passing a cost type and its connections.
/organizations/<organization_id>/bank-transactions # state is managed by classification (defaults to "backlog"), no need to set it.
# optionally classify inline with organization_bank_transaction_cost_type_id + connection_entities (see below).
curl -X POST 'https://api.enlivy.com/organizations/<organization_id>/bank-transactions' \
-H "Authorization: Bearer $ENLIVY_API_TOKEN" \
-H 'Accept: application/json' \
-H 'Content-Type: application/json' \
-d '{
"hash": "unique-dedupe-key",
"title": "Incoming transfer from Acme Studio",
"amount": 1200,
"currency": "EUR",
"organization_bank_account_id": "<bank_account_id>",
"sender_label": "ACME STUDIO SRL",
"note": "Invoice #2026-0142 settlement"
}'const baseUrl = "https://api.enlivy.com" ;
const token = process.env.ENLIVY_API_TOKEN;
const organizationId = "<organization_id>";
// state is managed by classification (defaults to "backlog"), no need to set it.
// optionally classify inline with organization_bank_transaction_cost_type_id + connection_entities (see below).
const result = await fetch(`${baseUrl}/organizations/${organizationId}/bank-transactions` , {
method: "POST",
headers: {
Authorization: `Bearer ${token}`,
Accept: "application/json" ,
"Content-Type": "application/json" ,
},
body: JSON.stringify({
// required; unique within a bank account (import de-dup key)
hash: "unique-dedupe-key",
title: "Incoming transfer from Acme Studio",
// money: signed decimal in MAJOR units (not cents)
amount: 1200,
// direction is DERIVED from the sign (positive = inbound, negative = outbound)
currency: "EUR",
organization_bank_account_id: "<bank_account_id>",
// free-text counterpart name from the bank feed
sender_label: "ACME STUDIO SRL",
note: "Invoice #2026-0142 settlement",
}),
}).then((response) => response.json());The heart of the workflow. Assign a cost type to classify the transaction, then connect it to the entities it settles. Enlivy advances the transaction’s state as you go: assigning a cost type marks it classified; connecting entities marks it connected (or connected_partially if the linked amounts don’t cover the full transaction).
/organizations/<organization_id>/bank-transactions/<bank_transaction_id> curl -X PUT 'https://api.enlivy.com/organizations/<organization_id>/bank-transactions/<bank_transaction_id>' \
-H "Authorization: Bearer $ENLIVY_API_TOKEN" \
-H 'Accept: application/json' \
-H 'Content-Type: application/json' \
-d '{
"organization_bank_transaction_cost_type_id": "<cost_type_id>",
"connection_entities": [
{
"connection_entity_type": "invoice",
"connection_entity_id": "<invoice_id>",
"amount": 1000
},
{
"connection_entity_type": "receipt",
"connection_entity_id": "<receipt_id>",
"amount": 200
}
]
}'const baseUrl = "https://api.enlivy.com" ;
const token = process.env.ENLIVY_API_TOKEN;
const organizationId = "<organization_id>";
const bankTransactionId = "<bank_transaction_id>";
const result = await fetch(`${baseUrl}/organizations/${organizationId}/bank-transactions/${bankTransactionId}` , {
method: "PUT",
headers: {
Authorization: `Bearer ${token}`,
Accept: "application/json" ,
"Content-Type": "application/json" ,
},
body: JSON.stringify({
// 1. Classify, assign a cost type. Moves state: backlog -> classified.
organization_bank_transaction_cost_type_id: "<cost_type_id>",
// 2. Reconcile, link the transaction to what it pays for.
// connection_entity_type must be one of the cost type's allowed connection_types.
// The array is an UPSERT:
// { connection_entity_type, connection_entity_id, amount } -> create a link
// { id } -> keep an existing link
// { id, _deleted: true } -> remove a link
// (omitting an existing link does NOT delete it)
// Amounts covering the full transaction -> "connected"; partial -> "connected_partially".
connection_entities: [
{
connection_entity_type: "invoice",
connection_entity_id: "<invoice_id>",
amount: 1000,
},
{
connection_entity_type: "receipt",
connection_entity_id: "<receipt_id>",
amount: 200,
},
],
}),
}).then((response) => response.json());Read aggregate cash-flow analytics across your transactions: a point-in-time summary or a history_monthly breakdown, scoped by account, direction, state, and date range, and convertible to a single reporting currency.
/organizations/<organization_id>/bank-transactions/analytics/summary # analyticsType: summary | history_monthly
curl -G 'https://api.enlivy.com/organizations/<organization_id>/bank-transactions/analytics/summary' \
--data-urlencode 'start_date=2026-01-01' \
--data-urlencode 'end_date=2026-12-31' \
--data-urlencode 'direction=outbound' \
--data-urlencode 'convert_to_currency=EUR' \
-H "Authorization: Bearer $ENLIVY_API_TOKEN" \
-H 'Accept: application/json' const baseUrl = "https://api.enlivy.com" ;
const token = process.env.ENLIVY_API_TOKEN;
const organizationId = "<organization_id>";
const analyticsType = "summary";
// analyticsType: summary | history_monthly
const query = new URLSearchParams({
start_date: "2026-01-01",
end_date: "2026-12-31",
direction: "outbound",
convert_to_currency: "EUR",
});
const result = await fetch(`${baseUrl}/organizations/${organizationId}/bank-transactions/analytics/${analyticsType}?${query}` , {
method: "GET",
headers: {
Authorization: `Bearer ${token}`,
Accept: "application/json" ,
},
}).then((response) => response.json());Soft-delete a transaction (its state becomes trashed). It can be restored later.
/organizations/<organization_id>/bank-transactions/<bank_transaction_id> # soft delete, restore via POST /bank-transactions/restore/{transactionId}
curl -X DELETE 'https://api.enlivy.com/organizations/<organization_id>/bank-transactions/<bank_transaction_id>' \
-H "Authorization: Bearer $ENLIVY_API_TOKEN" \
-H 'Accept: application/json' const baseUrl = "https://api.enlivy.com" ;
const token = process.env.ENLIVY_API_TOKEN;
const organizationId = "<organization_id>";
const bankTransactionId = "<bank_transaction_id>";
// soft delete, restore via POST /bank-transactions/restore/{transactionId}
const result = await fetch(`${baseUrl}/organizations/${organizationId}/bank-transactions/${bankTransactionId}` , {
method: "DELETE",
headers: {
Authorization: `Bearer ${token}`,
Accept: "application/json" ,
},
}).then((response) => response.json());API Reference
Tax Management from the API
Enlivy models tax the way Stripe models product tax codes, not one class per country. A tax class is a semantic product category (Standard goods & services, Books, Foodstuffs, Digital services, Exempt), and you tag each product with the category it is, never a percentage by hand. Underneath each class sits a set of tax rates, one per country or selling scenario, and at invoice time Enlivy resolves the single applicable rate from the buyer's country and tax status, taking the highest-priority match.
The examples below cover both layers: the class (the category) and the rates (the country rows that resolve underneath it).
List your product-tax categories. Include tax_rates_overview for a summary of the rate set resolving under each class.
/organizations/<organization_id>/tax-classes # filters: name, description, ids
# include tax_rates_overview for a per-class summary of the rate set
curl -G 'https://api.enlivy.com/organizations/<organization_id>/tax-classes' \
--data-urlencode 'page=1' \
--data-urlencode 'limit=20' \
--data-urlencode 'include=tax_rates_overview' \
-H "Authorization: Bearer $ENLIVY_API_TOKEN" \
-H 'Accept: application/json' const baseUrl = "https://api.enlivy.com" ;
const token = process.env.ENLIVY_API_TOKEN;
const organizationId = "<organization_id>";
// filters: name, description, ids
// include tax_rates_overview for a per-class summary of the rate set
const query = new URLSearchParams({
page: "1",
limit: "20",
include: "tax_rates_overview",
});
const result = await fetch(`${baseUrl}/organizations/${organizationId}/tax-classes?${query}` , {
method: "GET",
headers: {
Authorization: `Bearer ${token}`,
Accept: "application/json" ,
},
}).then((response) => response.json());Create one class per product category you sell. A class is a category, not a country and not a single rate; the country rows live underneath it. Products then point their organization_tax_class_id at the category they belong to.
/organizations/<organization_id>/tax-classes # Create one class per product CATEGORY you sell (Standard goods & services, Books,
# Foodstuffs, Accommodation, Digital services / SaaS, Exempt). A class is a category,
# not a country and not a single rate; the country rows live underneath it.
curl -X POST 'https://api.enlivy.com/organizations/<organization_id>/tax-classes' \
-H "Authorization: Bearer $ENLIVY_API_TOKEN" \
-H 'Accept: application/json' \
-H 'Content-Type: application/json' \
-d '{
"name_lang_map": {
"en": "Books & publications",
"ro": "Cărți și publicații"
},
"description_lang_map": {
"en": "Reduced-rate category for books and periodicals."
}
}'const baseUrl = "https://api.enlivy.com" ;
const token = process.env.ENLIVY_API_TOKEN;
const organizationId = "<organization_id>";
// Create one class per product CATEGORY you sell (Standard goods & services, Books,
// Foodstuffs, Accommodation, Digital services / SaaS, Exempt). A class is a category,
// not a country and not a single rate; the country rows live underneath it.
const result = await fetch(`${baseUrl}/organizations/${organizationId}/tax-classes` , {
method: "POST",
headers: {
Authorization: `Bearer ${token}`,
Accept: "application/json" ,
"Content-Type": "application/json" ,
},
body: JSON.stringify({
// the category, not a rate: per-locale maps
name_lang_map: {
en: "Books & publications",
ro: "Cărți și publicații",
},
description_lang_map: { en: "Reduced-rate category for books and periodicals." },
}),
}).then((response) => response.json());Update a category's labels, or soft-delete it (restore later). Deleting a class removes the category its rate rows resolve under, so remove or reassign those rows first.
/organizations/<organization_id>/tax-classes/<tax_class_id> +1# Update a category's labels
curl -X PUT 'https://api.enlivy.com/organizations/<organization_id>/tax-classes/<tax_class_id>' \
-H "Authorization: Bearer $ENLIVY_API_TOKEN" \
-H 'Accept: application/json' \
-H 'Content-Type: application/json' \
-d '{
"name_lang_map": {
"en": "Books & publications (2026)"
}
}'
# soft delete; restore via POST /tax-classes/restore/{taxClassId}
curl -X DELETE 'https://api.enlivy.com/organizations/<organization_id>/tax-classes/<tax_class_id>' \
-H "Authorization: Bearer $ENLIVY_API_TOKEN" \
-H 'Accept: application/json' const baseUrl = "https://api.enlivy.com" ;
const token = process.env.ENLIVY_API_TOKEN;
const organizationId = "<organization_id>";
const taxClassId = "<tax_class_id>";
// Update a category's labels
const result1 = await fetch(`${baseUrl}/organizations/${organizationId}/tax-classes/${taxClassId}` , {
method: "PUT",
headers: {
Authorization: `Bearer ${token}`,
Accept: "application/json" ,
"Content-Type": "application/json" ,
},
body: JSON.stringify({
name_lang_map: { en: "Books & publications (2026)" },
}),
}).then((response) => response.json());
// soft delete; restore via POST /tax-classes/restore/{taxClassId}
const result2 = await fetch(`${baseUrl}/organizations/${organizationId}/tax-classes/${taxClassId}` , {
method: "DELETE",
headers: {
Authorization: `Bearer ${token}`,
Accept: "application/json" ,
},
}).then((response) => response.json());List the rate rows. Expand the parent class and the locations each row applies to. For a per-class summary, use the class's tax_rates_overview include instead.
/organizations/<organization_id>/tax-rates # includes: organization, organization_tax_class, locations
curl -G 'https://api.enlivy.com/organizations/<organization_id>/tax-rates' \
--data-urlencode 'page=1' \
--data-urlencode 'limit=50' \
--data-urlencode 'include=organization_tax_class,locations' \
-H "Authorization: Bearer $ENLIVY_API_TOKEN" \
-H 'Accept: application/json' const baseUrl = "https://api.enlivy.com" ;
const token = process.env.ENLIVY_API_TOKEN;
const organizationId = "<organization_id>";
// includes: organization, organization_tax_class, locations
const query = new URLSearchParams({
page: "1",
limit: "50",
include: "organization_tax_class,locations",
});
const result = await fetch(`${baseUrl}/organizations/${organizationId}/tax-rates?${query}` , {
method: "GET",
headers: {
Authorization: `Bearer ${token}`,
Accept: "application/json" ,
},
}).then((response) => response.json());A tax rate is one row in a class's rate set, not a standalone tax. Each row declares a scope, an EN16931 VAT category, and a priority; at invoice time Enlivy takes the highest-priority matching row. The canonical set: home-country domestic, EU B2B reverse charge, per-country OSS, and a rest-of-world catch-all.
/organizations/<organization_id>/tax-rates +3# A tax rate is ONE ROW in a class's rate set, not a standalone tax. Each row declares a
# scope (locations and/or buyer conditions), an EN16931 VAT category (eu_vat_class), and a
# priority. At invoice time Enlivy takes the highest-priority row whose scope + buyer match.
# The canonical rate set for one category (home country = Romania): four archetypes.
#
# Home-country domestic: the default when selling within Romania
curl -X POST 'https://api.enlivy.com/organizations/<organization_id>/tax-rates' \
-H "Authorization: Bearer $ENLIVY_API_TOKEN" \
-H 'Accept: application/json' \
-H 'Content-Type: application/json' \
-d '{
"organization_tax_class_id": "<standard_goods_class_id>",
"has_eu_vat_properties": true,
"is_compound": false,
"is_inclusive": false,
"name": "RO Domestic 21%",
"rate": 21,
"priority": 1000,
"eu_vat_class": "S",
"has_locations": true,
"locations": [
{
"country_code": "RO"
}
]
}'
# EU B2B reverse charge: 0%, matches ONLY VAT-registered businesses in other EU states
curl -X POST 'https://api.enlivy.com/organizations/<organization_id>/tax-rates' \
-H "Authorization: Bearer $ENLIVY_API_TOKEN" \
-H 'Accept: application/json' \
-H 'Content-Type: application/json' \
-d '{
"organization_tax_class_id": "<standard_goods_class_id>",
"has_eu_vat_properties": true,
"is_compound": false,
"is_inclusive": false,
"name": "EU B2B Reverse Charge",
"rate": 0,
"priority": 900,
"eu_vat_class": "AE",
"vatex_code": "VATEX-EU-AE",
"is_business_entity": true,
"is_eu_vat_registered": true,
"has_locations": true,
"locations": [
{
"country_code": "DE"
},
{
"country_code": "FR"
}
]
}'
# Per-country EU domestic (B2C digital / OSS): charge the buyer's own country rate
curl -X POST 'https://api.enlivy.com/organizations/<organization_id>/tax-rates' \
-H "Authorization: Bearer $ENLIVY_API_TOKEN" \
-H 'Accept: application/json' \
-H 'Content-Type: application/json' \
-d '{
"organization_tax_class_id": "<standard_goods_class_id>",
"has_eu_vat_properties": true,
"is_compound": false,
"is_inclusive": false,
"name": "DE Domestic 19%",
"rate": 19,
"priority": 800,
"eu_vat_class": "S",
"has_locations": true,
"locations": [
{
"country_code": "DE"
}
]
}'
# Rest of world: outside scope, 0%, no locations = catch-all fallback
curl -X POST 'https://api.enlivy.com/organizations/<organization_id>/tax-rates' \
-H "Authorization: Bearer $ENLIVY_API_TOKEN" \
-H 'Accept: application/json' \
-H 'Content-Type: application/json' \
-d '{
"organization_tax_class_id": "<standard_goods_class_id>",
"has_eu_vat_properties": true,
"is_compound": false,
"is_inclusive": false,
"name": "Rest of World (Outside Scope)",
"rate": 0,
"priority": 100,
"eu_vat_class": "O",
"vatex_code": "VATEX-EU-O",
"has_locations": false
}'const baseUrl = "https://api.enlivy.com" ;
const token = process.env.ENLIVY_API_TOKEN;
const organizationId = "<organization_id>";
const headers = {
Authorization: `Bearer ${token}`,
Accept: "application/json" ,
"Content-Type": "application/json" ,
};
// A tax rate is ONE ROW in a class's rate set, not a standalone tax. Each row declares a
// scope (locations and/or buyer conditions), an EN16931 VAT category (eu_vat_class), and a
// priority. At invoice time Enlivy takes the highest-priority row whose scope + buyer match.
// The canonical rate set for one category (home country = Romania): four archetypes.
//
// Home-country domestic: the default when selling within Romania
const result1 = await fetch(`${baseUrl}/organizations/${organizationId}/tax-rates` , {
method: "POST",
headers,
body: JSON.stringify({
organization_tax_class_id: "<standard_goods_class_id>",
has_eu_vat_properties: true,
is_compound: false,
is_inclusive: false,
name: "RO Domestic 21%",
rate: 21,
priority: 1000,
// Standard-rated, stamped on the invoice line
eu_vat_class: "S",
has_locations: true,
locations: [{ country_code: "RO" }],
}),
}).then((response) => response.json());
// EU B2B reverse charge: 0%, matches ONLY VAT-registered businesses in other EU states
const result2 = await fetch(`${baseUrl}/organizations/${organizationId}/tax-rates` , {
method: "POST",
headers,
body: JSON.stringify({
organization_tax_class_id: "<standard_goods_class_id>",
has_eu_vat_properties: true,
is_compound: false,
is_inclusive: false,
name: "EU B2B Reverse Charge",
rate: 0,
priority: 900,
eu_vat_class: "AE",
// exemption reason (PEPPOL)
vatex_code: "VATEX-EU-AE",
// buyer-qualification gates
is_business_entity: true,
is_eu_vat_registered: true,
has_locations: true,
locations: [{ country_code: "DE" }, { country_code: "FR" }],
}),
}).then((response) => response.json());
// Per-country EU domestic (B2C digital / OSS): charge the buyer's own country rate
const result3 = await fetch(`${baseUrl}/organizations/${organizationId}/tax-rates` , {
method: "POST",
headers,
body: JSON.stringify({
organization_tax_class_id: "<standard_goods_class_id>",
has_eu_vat_properties: true,
is_compound: false,
is_inclusive: false,
name: "DE Domestic 19%",
rate: 19,
priority: 800,
eu_vat_class: "S",
has_locations: true,
locations: [{ country_code: "DE" }],
}),
}).then((response) => response.json());
// Rest of world: outside scope, 0%, no locations = catch-all fallback
const result4 = await fetch(`${baseUrl}/organizations/${organizationId}/tax-rates` , {
method: "POST",
headers,
body: JSON.stringify({
organization_tax_class_id: "<standard_goods_class_id>",
has_eu_vat_properties: true,
is_compound: false,
is_inclusive: false,
name: "Rest of World (Outside Scope)",
rate: 0,
priority: 100,
eu_vat_class: "O",
vatex_code: "VATEX-EU-O",
has_locations: false,
}),
}).then((response) => response.json());Adjust a single row: change its percentage, VAT category, scope, or priority in the resolution ladder. Send only the fields you are changing.
/organizations/<organization_id>/tax-rates/<tax_rate_id> # Adjust a single row: its percentage, VAT category, scope, or priority in the ladder.
# Send only the fields you are changing.
curl -X PUT 'https://api.enlivy.com/organizations/<organization_id>/tax-rates/<tax_rate_id>' \
-H "Authorization: Bearer $ENLIVY_API_TOKEN" \
-H 'Accept: application/json' \
-H 'Content-Type: application/json' \
-d '{
"rate": 20,
"display_name": "VAT 20%"
}'const baseUrl = "https://api.enlivy.com" ;
const token = process.env.ENLIVY_API_TOKEN;
const organizationId = "<organization_id>";
const taxRateId = "<tax_rate_id>";
// Adjust a single row: its percentage, VAT category, scope, or priority in the ladder.
// Send only the fields you are changing.
const result = await fetch(`${baseUrl}/organizations/${organizationId}/tax-rates/${taxRateId}` , {
method: "PUT",
headers: {
Authorization: `Bearer ${token}`,
Accept: "application/json" ,
"Content-Type": "application/json" ,
},
body: JSON.stringify({
// e.g. the RO domestic rate changes
rate: 20,
display_name: "VAT 20%",
}),
}).then((response) => response.json());Soft-delete a single row from a class's set, for example to retire a country you no longer sell into. It can be restored later.
/organizations/<organization_id>/tax-rates/<tax_rate_id> # soft delete; restore via POST /tax-rates/restore/{taxRateId}
curl -X DELETE 'https://api.enlivy.com/organizations/<organization_id>/tax-rates/<tax_rate_id>' \
-H "Authorization: Bearer $ENLIVY_API_TOKEN" \
-H 'Accept: application/json' const baseUrl = "https://api.enlivy.com" ;
const token = process.env.ENLIVY_API_TOKEN;
const organizationId = "<organization_id>";
const taxRateId = "<tax_rate_id>";
// soft delete; restore via POST /tax-rates/restore/{taxRateId}
const result = await fetch(`${baseUrl}/organizations/${organizationId}/tax-rates/${taxRateId}` , {
method: "DELETE",
headers: {
Authorization: `Bearer ${token}`,
Accept: "application/json" ,
},
}).then((response) => response.json());API Reference
Prospect Management from the API
Wire your lead capture, CRM, and sales automation straight into Enlivy. The Prospects API lets you work with your sales pipelines: create leads from any source, move them through configured stages, and log each touchpoint.
The examples below cover listing and filtering Prospects, creating a lead, moving it through configured stages, and recording activities such as calls and meetings. The API also exposes boards, duplicate review and merge, and a downloadable brief.
Pull your pipeline into any dashboard or sync job. Query prospects with pagination, filter by stage, owner, source, or date range, and full-text search by name, company, or email. Expand related records such as the current stage and the assigned team member.
/organizations/<organization_id>/prospects curl -G 'https://api.enlivy.com/organizations/<organization_id>/prospects' \
--data-urlencode 'page=1' \
--data-urlencode 'limit=20' \
--data-urlencode 'source_type=inbound' \
--data-urlencode 'include=organization_prospect_stage,assigned_organization_user' \
--data-urlencode 'include_meta=navigation' \
-H "Authorization: Bearer $ENLIVY_API_TOKEN" \
-H 'Accept: application/json' const baseUrl = "https://api.enlivy.com" ;
const token = process.env.ENLIVY_API_TOKEN;
const organizationId = "<organization_id>";
const query = new URLSearchParams({
page: "1",
limit: "20",
source_type: "inbound",
include: "organization_prospect_stage,assigned_organization_user",
include_meta: "navigation",
});
const result = await fetch(`${baseUrl}/organizations/${organizationId}/prospects?${query}` , {
method: "GET",
headers: {
Authorization: `Bearer ${token}`,
Accept: "application/json" ,
},
}).then((response) => response.json());Retrieve a single prospect with everything attached to it: contact details, source attribution, budget, current pipeline stage, and lifecycle timestamps such as qualified, won, and lost.
/organizations/<organization_id>/prospects/<prospect_id> curl -G 'https://api.enlivy.com/organizations/<organization_id>/prospects/<prospect_id>' \
--data-urlencode 'include=organization_prospect_stage,assigned_organization_user' \
-H "Authorization: Bearer $ENLIVY_API_TOKEN" \
-H 'Accept: application/json' const baseUrl = "https://api.enlivy.com" ;
const token = process.env.ENLIVY_API_TOKEN;
const organizationId = "<organization_id>";
const prospectId = "<prospect_id>";
const query = new URLSearchParams({
include: "organization_prospect_stage,assigned_organization_user",
});
const result = await fetch(`${baseUrl}/organizations/${organizationId}/prospects/${prospectId}?${query}` , {
method: "GET",
headers: {
Authorization: `Bearer ${token}`,
Accept: "application/json" ,
},
}).then((response) => response.json());Capture a lead from any source: a landing page, an ad campaign, an outbound list, or your own app. Send a person, a company, or both, tag where it came from, and drop it straight onto your pipeline.
/organizations/<organization_id>/prospects curl -X POST 'https://api.enlivy.com/organizations/<organization_id>/prospects' \
-H "Authorization: Bearer $ENLIVY_API_TOKEN" \
-H 'Accept: application/json' \
-H 'Content-Type: application/json' \
-d '{
"first_name": "Jane",
"last_name": "Doe",
"company_name": "Acme Studio",
"email": "jane@acme.example",
"source_type": "inbound",
"source_channel": "website",
"source_campaign": "summer-launch",
"summary": "Requested a demo from the pricing page.",
"budget": "5000",
"budget_currency": "EUR"
}'const baseUrl = "https://api.enlivy.com" ;
const token = process.env.ENLIVY_API_TOKEN;
const organizationId = "<organization_id>";
const result = await fetch(`${baseUrl}/organizations/${organizationId}/prospects` , {
method: "POST",
headers: {
Authorization: `Bearer ${token}`,
Accept: "application/json" ,
"Content-Type": "application/json" ,
},
body: JSON.stringify({
first_name: "Jane",
last_name: "Doe",
company_name: "Acme Studio",
email: "jane@acme.example",
source_type: "inbound",
source_channel: "website",
source_campaign: "summer-launch",
summary: "Requested a demo from the pricing page.",
budget: "5000",
budget_currency: "EUR",
}),
}).then((response) => response.json());Update a prospect’s contact details, owner, budget, summary, or lifecycle state (qualified, disqualified, won, lost). You only send the fields you want to change.
/organizations/<organization_id>/prospects/<prospect_id> curl -X PUT 'https://api.enlivy.com/organizations/<organization_id>/prospects/<prospect_id>' \
-H "Authorization: Bearer $ENLIVY_API_TOKEN" \
-H 'Accept: application/json' \
-H 'Content-Type: application/json' \
-d '{
"summary": "Budget confirmed, decision expected next week.",
"budget": "8000",
"budget_currency": "EUR"
}'const baseUrl = "https://api.enlivy.com" ;
const token = process.env.ENLIVY_API_TOKEN;
const organizationId = "<organization_id>";
const prospectId = "<prospect_id>";
const result = await fetch(`${baseUrl}/organizations/${organizationId}/prospects/${prospectId}` , {
method: "PUT",
headers: {
Authorization: `Bearer ${token}`,
Accept: "application/json" ,
"Content-Type": "application/json" ,
},
body: JSON.stringify({
summary: "Budget confirmed, decision expected next week.",
budget: "8000",
budget_currency: "EUR",
}),
}).then((response) => response.json());Move a prospect along a configured stage path. Each advance records who performed it and can include an outcome, note, and linked report. The advance also appears on the prospect timeline.
/organizations/<organization_id>/prospects/<prospect_id>/advance curl -X POST 'https://api.enlivy.com/organizations/<organization_id>/prospects/<prospect_id>/advance' \
-H "Authorization: Bearer $ENLIVY_API_TOKEN" \
-H 'Accept: application/json' \
-H 'Content-Type: application/json' \
-d '{
"organization_prospect_stage_path_id": "stage_path_id_here",
"outcome": "Qualified, budget and timeline confirmed.",
"description": "Moved from New to Qualified after the discovery call."
}'const baseUrl = "https://api.enlivy.com" ;
const token = process.env.ENLIVY_API_TOKEN;
const organizationId = "<organization_id>";
const prospectId = "<prospect_id>";
const result = await fetch(`${baseUrl}/organizations/${organizationId}/prospects/${prospectId}/advance` , {
method: "POST",
headers: {
Authorization: `Bearer ${token}`,
Accept: "application/json" ,
"Content-Type": "application/json" ,
},
body: JSON.stringify({
organization_prospect_stage_path_id: "stage_path_id_here",
outcome: "Qualified, budget and timeline confirmed.",
description: "Moved from New to Qualified after the discovery call.",
}),
}).then((response) => response.json());Record a touchpoint on a prospect: a call, a meeting, an email, a note. Activities build the prospect’s timeline and give your team a shared, chronological history of every interaction.
/organizations/<organization_id>/prospect-activities curl -X POST 'https://api.enlivy.com/organizations/<organization_id>/prospect-activities' \
-H "Authorization: Bearer $ENLIVY_API_TOKEN" \
-H 'Accept: application/json' \
-H 'Content-Type: application/json' \
-d '{
"organization_prospect_id": "prospect_id_here",
"title": "Discovery call",
"description": "Walked through pricing and the onboarding timeline.",
"outcome": "Positive, sending a proposal next week.",
"activity_at": "2026-07-03 15:30:00"
}'const baseUrl = "https://api.enlivy.com" ;
const token = process.env.ENLIVY_API_TOKEN;
const organizationId = "<organization_id>";
const result = await fetch(`${baseUrl}/organizations/${organizationId}/prospect-activities` , {
method: "POST",
headers: {
Authorization: `Bearer ${token}`,
Accept: "application/json" ,
"Content-Type": "application/json" ,
},
body: JSON.stringify({
organization_prospect_id: "prospect_id_here",
title: "Discovery call",
description: "Walked through pricing and the onboarding timeline.",
outcome: "Positive, sending a proposal next week.",
activity_at: "2026-07-03 15:30:00",
}),
}).then((response) => response.json());Read the activity feed across your organization. Expand each entry with the Prospect it belongs to, the team member who performed it, and any linked report or stage transition.
/organizations/<organization_id>/prospect-activities curl -G 'https://api.enlivy.com/organizations/<organization_id>/prospect-activities' \
--data-urlencode 'page=1' \
--data-urlencode 'limit=20' \
--data-urlencode 'include=organization_prospect,performed_by_organization_user' \
--data-urlencode 'include_meta=navigation' \
-H "Authorization: Bearer $ENLIVY_API_TOKEN" \
-H 'Accept: application/json' const baseUrl = "https://api.enlivy.com" ;
const token = process.env.ENLIVY_API_TOKEN;
const organizationId = "<organization_id>";
const query = new URLSearchParams({
page: "1",
limit: "20",
include: "organization_prospect,performed_by_organization_user",
include_meta: "navigation",
});
const result = await fetch(`${baseUrl}/organizations/${organizationId}/prospect-activities?${query}` , {
method: "GET",
headers: {
Authorization: `Bearer ${token}`,
Accept: "application/json" ,
},
}).then((response) => response.json());API Reference
Contract Management from the API
Draft, send, sign, and track contracts end-to-end, without your users ever leaving your product. The Contracts API covers the whole lifecycle: assemble a contract from chapters and parties, move it through your own status pipeline, open a legally-binding signing session per signer, and pull the signed document plus its tamper-evident audit evidence back out.
Everything is organized around four resources: contracts (the document, its chapters and its parties), contract statuses (your configurable pipeline stages), signing sessions (one per party, with email / SMS verification), and the audit trail (delivery logs and signing evidence). The examples below walk the entire flow.
Query your contracts with pagination, rich filtering, and full-text search. Filter by status, sender, receiver, direction (inbound / outbound), category (core, amendment, addenda, supplement), source (internal / uploaded), locale, parent contract, or any of the date ranges (issued_at, ends_at, created_at, updated_at). Expand related records inline.
/organizations/<organization_id>/contracts # filters are optional and combinable
# pass a q=... param instead of ids to run a full-text search
curl -G 'https://api.enlivy.com/organizations/<organization_id>/contracts' \
--data-urlencode 'page=1' \
--data-urlencode 'limit=20' \
--data-urlencode 'direction=outbound' \
--data-urlencode 'category=core' \
--data-urlencode 'include=contract_status,sender_user,receiver_user,contract_parties' \
--data-urlencode 'include_meta=navigation' \
-H "Authorization: Bearer $ENLIVY_API_TOKEN" \
-H 'Accept: application/json' const baseUrl = "https://api.enlivy.com" ;
const token = process.env.ENLIVY_API_TOKEN;
const organizationId = "<organization_id>";
// filters are optional and combinable
// pass a q=... param instead of ids to run a full-text search
const query = new URLSearchParams({
page: "1",
limit: "20",
direction: "outbound",
category: "core",
include: "contract_status,sender_user,receiver_user,contract_parties",
include_meta: "navigation",
});
const result = await fetch(`${baseUrl}/organizations/${organizationId}/contracts?${query}` , {
method: "GET",
headers: {
Authorization: `Bearer ${token}`,
Accept: "application/json" ,
},
}).then((response) => response.json());Retrieve a single contract with everything attached: its chapters, its parties, the current pipeline status, the linked file, and the sender / receiver.
/organizations/<organization_id>/contracts/<contract_id> # available includes: organization, parent_contract, sender_user, receiver_user,
# file, contract_status, contract_chapters, contract_parties, contract_prefix
curl -G 'https://api.enlivy.com/organizations/<organization_id>/contracts/<contract_id>' \
--data-urlencode 'include=contract_chapters,contract_parties,contract_status' \
-H "Authorization: Bearer $ENLIVY_API_TOKEN" \
-H 'Accept: application/json' const baseUrl = "https://api.enlivy.com" ;
const token = process.env.ENLIVY_API_TOKEN;
const organizationId = "<organization_id>";
const contractId = "<contract_id>";
// available includes: organization, parent_contract, sender_user, receiver_user,
// file, contract_status, contract_chapters, contract_parties, contract_prefix
const query = new URLSearchParams({
include: "contract_chapters,contract_parties,contract_status",
});
const result = await fetch(`${baseUrl}/organizations/${organizationId}/contracts/${contractId}?${query}` , {
method: "GET",
headers: {
Authorization: `Bearer ${token}`,
Accept: "application/json" ,
},
}).then((response) => response.json());Build a contract in one call: its metadata, its body as ordered chapters, and its parties. A party can be an individual or an organization, and each carries how it should be referenced in the document and whether its signature is required.
/organizations/<organization_id>/contracts curl -X POST 'https://api.enlivy.com/organizations/<organization_id>/contracts' \
-H "Authorization: Bearer $ENLIVY_API_TOKEN" \
-H 'Accept: application/json' \
-H 'Content-Type: application/json' \
-d '{
"category": "core",
"source": "internal",
"direction": "outbound",
"organization_sender_user_id": "<org_user_sender_id>",
"organization_receiver_user_id": "<org_user_receiver_id>",
"organization_contract_status_id": "<contract_status_id>",
"organization_contract_prefix_id": "<contract_prefix_id>",
"title": "Master Services Agreement",
"sub_title": "2026 Engagement",
"locale": "en",
"issued_at": "2026-07-03 09:00:00",
"ends_at": "2027-07-03 09:00:00",
"content_introduction": "This agreement is entered into between the parties below.",
"content_signature_disclaimer": "By signing, each party agrees to the terms above.",
"chapters": [
{
"title": "Scope of Work",
"content": "<p>The Provider will deliver…</p>",
"order": 1
},
{
"title": "Payment Terms",
"content": "<p>Fees are due within 14 days…</p>",
"order": 2
}
],
"parties": [
{
"party_type": "organization",
"party_country_code": "RO",
"organization_name": "Acme Studio SRL",
"organization_type": "srl",
"first_name": "Jane",
"last_name": "Doe",
"role_in_organization": "Administrator",
"referenced_as_within_document": "the Provider",
"appears_as_party": true,
"is_signature_required": true,
"contact_email_address": "jane@acme.example",
"order": 0
},
{
"party_type": "individual",
"party_country_code": "RO",
"first_name": "John",
"last_name": "Smith",
"referenced_as_within_document": "the Client",
"appears_as_party": true,
"is_signature_required": true,
"contact_email_address": "john@example.com",
"order": 1
}
]
}' const baseUrl = "https://api.enlivy.com" ;
const token = process.env.ENLIVY_API_TOKEN;
const organizationId = "<organization_id>";
const result = await fetch(`${baseUrl}/organizations/${organizationId}/contracts` , {
method: "POST",
headers: {
Authorization: `Bearer ${token}`,
Accept: "application/json" ,
"Content-Type": "application/json" ,
},
body: JSON.stringify({
// core | amendment | addenda | supplement.
// amendment/addenda/supplement REQUIRE organization_contract_id -> a "core" parent contract.
category: "core",
// internal -> auto-numbered from a prefix (organization_contract_prefix_id required below)
// uploaded -> you supply `number`, and may set `signed_by_all_parties_at`
source: "internal",
// outbound | inbound
direction: "outbound",
organization_sender_user_id: "<org_user_sender_id>",
organization_receiver_user_id: "<org_user_receiver_id>",
organization_contract_status_id: "<contract_status_id>",
// required only for internal + core; omit it for any other source/category
organization_contract_prefix_id: "<contract_prefix_id>",
title: "Master Services Agreement",
sub_title: "2026 Engagement",
locale: "en",
issued_at: "2026-07-03 09:00:00",
ends_at: "2027-07-03 09:00:00",
content_introduction: "This agreement is entered into between the parties below.",
content_signature_disclaimer: "By signing, each party agrees to the terms above.",
chapters: [
{
title: "Scope of Work",
content: "<p>The Provider will deliver…</p>" ,
order: 1,
},
{
title: "Payment Terms",
content: "<p>Fees are due within 14 days…</p>" ,
order: 2,
},
],
parties: [
// country-specific `information` (person) and `organization_information` (company)
// JSON blocks are also accepted; their shape is validated against the country schema.
{
// party_type, party_country_code, first_name, last_name are required on every party
// organization | individual
party_type: "organization",
party_country_code: "RO",
// organization parties additionally REQUIRE organization_name + organization_type
organization_name: "Acme Studio SRL",
// value comes from reference data
organization_type: "srl",
first_name: "Jane",
last_name: "Doe",
role_in_organization: "Administrator",
referenced_as_within_document: "the Provider",
appears_as_party: true,
is_signature_required: true,
contact_email_address: "jane@acme.example",
order: 0,
},
{
party_type: "individual",
party_country_code: "RO",
first_name: "John",
last_name: "Smith",
referenced_as_within_document: "the Client",
appears_as_party: true,
is_signature_required: true,
contact_email_address: "john@example.com",
order: 1,
},
],
}),
}).then((response) => response.json());Update a contract’s metadata, move it to another status, adjust its chapters, or refine its parties. Send only what you want to change.
/organizations/<organization_id>/contracts/<contract_id> curl -X PUT 'https://api.enlivy.com/organizations/<organization_id>/contracts/<contract_id>' \
-H "Authorization: Bearer $ENLIVY_API_TOKEN" \
-H 'Accept: application/json' \
-H 'Content-Type: application/json' \
-d '{
"organization_contract_status_id": "<next_status_id>",
"sub_title": "2026 Engagement (revised)",
"ends_at": "2027-12-31 23:59:59"
}'const baseUrl = "https://api.enlivy.com" ;
const token = process.env.ENLIVY_API_TOKEN;
const organizationId = "<organization_id>";
const contractId = "<contract_id>";
const result = await fetch(`${baseUrl}/organizations/${organizationId}/contracts/${contractId}` , {
method: "PUT",
headers: {
Authorization: `Bearer ${token}`,
Accept: "application/json" ,
"Content-Type": "application/json" ,
},
body: JSON.stringify({
// partial update, only the fields you send are touched
organization_contract_status_id: "<next_status_id>",
sub_title: "2026 Engagement (revised)",
ends_at: "2027-12-31 23:59:59",
}),
}).then((response) => response.json());Render and download the contract as a PDF, the same document your counterparties see.
/organizations/<organization_id>/contracts/<contract_id>/download # returns a file stream, read it as a blob, not JSON
curl 'https://api.enlivy.com/organizations/<organization_id>/contracts/<contract_id>/download' \
-H "Authorization: Bearer $ENLIVY_API_TOKEN" \
--remote-name --remote-header-nameconst baseUrl = "https://api.enlivy.com" ;
const token = process.env.ENLIVY_API_TOKEN;
const organizationId = "<organization_id>";
const contractId = "<contract_id>";
// returns a file stream, read it as a blob, not JSON
const pdf = await fetch(`${baseUrl}/organizations/${organizationId}/contracts/${contractId}/download` , {
method: "GET",
headers: {
Authorization: `Bearer ${token}`,
},
}).then((response) => response.blob());Soft-delete a contract. It can be restored later.
/organizations/<organization_id>/contracts/<contract_id> # soft delete, restore via POST /contracts/restore/{contractId}
curl -X DELETE 'https://api.enlivy.com/organizations/<organization_id>/contracts/<contract_id>' \
-H "Authorization: Bearer $ENLIVY_API_TOKEN" \
-H 'Accept: application/json' const baseUrl = "https://api.enlivy.com" ;
const token = process.env.ENLIVY_API_TOKEN;
const organizationId = "<organization_id>";
const contractId = "<contract_id>";
// soft delete, restore via POST /contracts/restore/{contractId}
const result = await fetch(`${baseUrl}/organizations/${organizationId}/contracts/${contractId}` , {
method: "DELETE",
headers: {
Authorization: `Bearer ${token}`,
Accept: "application/json" ,
},
}).then((response) => response.json());Contracts move through your pipeline, not a fixed one. Each status maps to a lifecycle state (draft, action_required, accepted, breach, or terminated), carries localized labels, a color, and an order, and applies to inbound, outbound, or any direction. List them to build a board or drive automation.
/organizations/<organization_id>/contract-statuses curl -G 'https://api.enlivy.com/organizations/<organization_id>/contract-statuses' \
--data-urlencode 'page=1' \
--data-urlencode 'limit=50' \
-H "Authorization: Bearer $ENLIVY_API_TOKEN" \
-H 'Accept: application/json' const baseUrl = "https://api.enlivy.com" ;
const token = process.env.ENLIVY_API_TOKEN;
const organizationId = "<organization_id>";
const query = new URLSearchParams({
page: "1",
limit: "50",
});
const result = await fetch(`${baseUrl}/organizations/${organizationId}/contract-statuses?${query}` , {
method: "GET",
headers: {
Authorization: `Bearer ${token}`,
Accept: "application/json" ,
},
}).then((response) => response.json());Define a new pipeline stage. Labels and descriptions are per-locale language maps, contract_state ties the stage to a lifecycle state, and you can auto-advance to another status once an action succeeds.
/organizations/<organization_id>/contract-statuses curl -X POST 'https://api.enlivy.com/organizations/<organization_id>/contract-statuses' \
-H "Authorization: Bearer $ENLIVY_API_TOKEN" \
-H 'Accept: application/json' \
-H 'Content-Type: application/json' \
-d '{
"title_lang_map": {
"en": "Awaiting Signature",
"ro": "În așteptarea semnării"
},
"description_lang_map": {
"en": "Sent to all parties for signing.",
"ro": "Trimis către toate părțile pentru semnare."
},
"contract_state": "action_required",
"direction": "any",
"order": 3,
"rgba_color_code": "rgba(59, 130, 246, 1)"
}'const baseUrl = "https://api.enlivy.com" ;
const token = process.env.ENLIVY_API_TOKEN;
const organizationId = "<organization_id>";
const result = await fetch(`${baseUrl}/organizations/${organizationId}/contract-statuses` , {
method: "POST",
headers: {
Authorization: `Bearer ${token}`,
Accept: "application/json" ,
"Content-Type": "application/json" ,
},
body: JSON.stringify({
// per-locale maps, not plain strings
title_lang_map: {
en: "Awaiting Signature",
ro: "În așteptarea semnării",
},
description_lang_map: {
en: "Sent to all parties for signing.",
ro: "Trimis către toate părțile pentru semnare.",
},
// draft | action_required | accepted | breach | terminated
contract_state: "action_required",
// inbound | outbound | any
direction: "any",
// order is unique per organization, pick a free slot,
// or reorder existing stages via PUT /contract-statuses/reorder
order: 3,
rgba_color_code: "rgba(59, 130, 246, 1)",
}),
}).then((response) => response.json());A signing session is created per party that needs to sign. Choose the accepted signature types (draw, checkbox, classic file, electronic file), which confirmations are required (email, phone, legally-binding acknowledgement), and an expiry. With signature_source user_flow, Enlivy hosts the guided signer experience and returns a tokenized signing URL.
/organizations/<organization_id>/contract-signatures curl -X POST 'https://api.enlivy.com/organizations/<organization_id>/contract-signatures' \
-H "Authorization: Bearer $ENLIVY_API_TOKEN" \
-H 'Accept: application/json' \
-H 'Content-Type: application/json' \
-d '{
"organization_contract_id": "<contract_id>",
"organization_contract_party_id": "<party_id>",
"signature_source": "user_flow",
"sign_session_signature_types": [
"draw",
"checkbox"
],
"sign_session_required_confirmations": [
"email",
"legally_binding"
],
"expires_at": "2026-07-17 23:59:59"
}'const baseUrl = "https://api.enlivy.com" ;
const token = process.env.ENLIVY_API_TOKEN;
const organizationId = "<organization_id>";
const result = await fetch(`${baseUrl}/organizations/${organizationId}/contract-signatures` , {
method: "POST",
headers: {
Authorization: `Bearer ${token}`,
Accept: "application/json" ,
"Content-Type": "application/json" ,
},
body: JSON.stringify({
organization_contract_id: "<contract_id>",
// one active session per party, organization_contract_party_id is unique
organization_contract_party_id: "<party_id>",
// user_flow -> Enlivy hosts the signer experience (get the link via sign_session_url)
// admin_panel -> you record the signature yourself and may send is_signed / signature_drawing / signed_contract
signature_source: "user_flow",
sign_session_signature_types: [
"draw",
// draw | checkbox | file_classic | file_electronic
"checkbox",
],
sign_session_required_confirmations: [
"email",
// email | phone | legally_binding
"legally_binding",
],
expires_at: "2026-07-17 23:59:59",
}),
}).then((response) => response.json());Deliver the signing session to the party by email or SMS, with an optional custom message. Every send is recorded in the notification log for audit.
/organizations/<organization_id>/contract-signatures/<contract_signature_id>/send curl -X POST 'https://api.enlivy.com/organizations/<organization_id>/contract-signatures/<contract_signature_id>/send' \
-H "Authorization: Bearer $ENLIVY_API_TOKEN" \
-H 'Accept: application/json' \
-H 'Content-Type: application/json' \
-d '{
"method": "email",
"message": "Please review and sign the agreement by Friday."
}'const baseUrl = "https://api.enlivy.com" ;
const token = process.env.ENLIVY_API_TOKEN;
const organizationId = "<organization_id>";
const contractSignatureId = "<contract_signature_id>";
const result = await fetch(`${baseUrl}/organizations/${organizationId}/contract-signatures/${contractSignatureId}/send` , {
method: "POST",
headers: {
Authorization: `Bearer ${token}`,
Accept: "application/json" ,
"Content-Type": "application/json" ,
},
body: JSON.stringify({
// email | sms
method: "email",
message: "Please review and sign the agreement by Friday.",
}),
}).then((response) => response.json());Poll a session for its status, whether it’s signed, its expiry, and which evidence artifacts exist. Include sign_session_url to get the hosted signing link to hand to the signer.
/organizations/<organization_id>/contract-signatures/<contract_signature_id> # sign_session_url returns the hosted link to give to the signer.
# status is one of: pending | sent | completed | expired | void
curl -G 'https://api.enlivy.com/organizations/<organization_id>/contract-signatures/<contract_signature_id>' \
--data-urlencode 'include=sign_session_url,organization_contract' \
-H "Authorization: Bearer $ENLIVY_API_TOKEN" \
-H 'Accept: application/json' const baseUrl = "https://api.enlivy.com" ;
const token = process.env.ENLIVY_API_TOKEN;
const organizationId = "<organization_id>";
const contractSignatureId = "<contract_signature_id>";
// sign_session_url returns the hosted link to give to the signer.
// status is one of: pending | sent | completed | expired | void
const query = new URLSearchParams({
include: "sign_session_url,organization_contract",
});
const result = await fetch(`${baseUrl}/organizations/${organizationId}/contract-signatures/${contractSignatureId}?${query}` , {
method: "GET",
headers: {
Authorization: `Bearer ${token}`,
Accept: "application/json" ,
},
}).then((response) => response.json());List every signing session, optionally scoped to a single contract, the quickest way to see who has signed and who is still pending across all parties.
/organizations/<organization_id>/contract-signatures # omit organization_contract_id to list sessions across every contract
curl -G 'https://api.enlivy.com/organizations/<organization_id>/contract-signatures' \
--data-urlencode 'organization_contract_id=<contract_id>' \
--data-urlencode 'include=sign_session_url' \
-H "Authorization: Bearer $ENLIVY_API_TOKEN" \
-H 'Accept: application/json' const baseUrl = "https://api.enlivy.com" ;
const token = process.env.ENLIVY_API_TOKEN;
const organizationId = "<organization_id>";
// omit organization_contract_id to list sessions across every contract
const query = new URLSearchParams({
organization_contract_id: "<contract_id>",
include: "sign_session_url",
});
const result = await fetch(`${baseUrl}/organizations/${organizationId}/contract-signatures?${query}` , {
method: "GET",
headers: {
Authorization: `Bearer ${token}`,
Accept: "application/json" ,
},
}).then((response) => response.json());Pull the tamper-evident evidence package (authentication, consent, and signature biometrics captured during signing) for compliance and dispute handling.
/organizations/<organization_id>/contracts/<contract_id>/download-evidence # also available per session: /contract-signatures/{signatureId}/download-evidence
curl 'https://api.enlivy.com/organizations/<organization_id>/contracts/<contract_id>/download-evidence' \
-H "Authorization: Bearer $ENLIVY_API_TOKEN" \
--remote-name --remote-header-nameconst baseUrl = "https://api.enlivy.com" ;
const token = process.env.ENLIVY_API_TOKEN;
const organizationId = "<organization_id>";
const contractId = "<contract_id>";
// also available per session: /contract-signatures/{signatureId}/download-evidence
const evidence = await fetch(`${baseUrl}/organizations/${organizationId}/contracts/${contractId}/download-evidence` , {
method: "GET",
headers: {
Authorization: `Bearer ${token}`,
},
}).then((response) => response.blob());API Reference
Product Management from the API
Keep your catalog in sync from your own systems. The Products API gives you programmatic control over the reusable line items you sell (services, digital goods, physical products, and bonuses), each with multi-currency pricing, per-locale names, tax class, and the metadata needed for compliant e-invoicing. Products created here drop straight into invoices, proposals, and billing schedules.
The examples below cover the core operations: listing and searching products, creating one, editing it, and removing it.
Query your catalog with pagination, filtering, and full-text search. Filter by name or description, fetch specific ids, and expand the tax class inline.
/organizations/<organization_id>/products # filters: name, description, ids (or q=... for full-text search)
# includes: organization, tax_class, tag_ids, deleted_by_user
curl -G 'https://api.enlivy.com/organizations/<organization_id>/products' \
--data-urlencode 'page=1' \
--data-urlencode 'limit=20' \
--data-urlencode 'name=Consulting' \
--data-urlencode 'include=tax_class' \
-H "Authorization: Bearer $ENLIVY_API_TOKEN" \
-H 'Accept: application/json' const baseUrl = "https://api.enlivy.com" ;
const token = process.env.ENLIVY_API_TOKEN;
const organizationId = "<organization_id>";
// filters: name, description, ids (or q=... for full-text search)
// includes: organization, tax_class, tag_ids, deleted_by_user
const query = new URLSearchParams({
page: "1",
limit: "20",
name: "Consulting",
include: "tax_class",
});
const result = await fetch(`${baseUrl}/organizations/${organizationId}/products?${query}` , {
method: "GET",
headers: {
Authorization: `Bearer ${token}`,
Accept: "application/json" ,
},
}).then((response) => response.json());Add a product to your catalog. A product needs a type and a price_map; everything else (localized names, tax class, barcodes, and e-invoicing metadata) is optional.
/organizations/<organization_id>/products curl -X POST 'https://api.enlivy.com/organizations/<organization_id>/products' \
-H "Authorization: Bearer $ENLIVY_API_TOKEN" \
-H 'Accept: application/json' \
-H 'Content-Type: application/json' \
-d '{
"type": "service",
"price_map": {
"EUR": "49.90",
"USD": "54.00"
},
"primary_currency": "EUR",
"name_lang_map": {
"en": "Consulting Hour",
"ro": "Oră de consultanță"
},
"unit_code": "HUR",
"description_lang_map": {
"en": "One hour of advisory services."
},
"alias": "consulting-hour",
"organization_tax_class_id": "<tax_class_id>",
"is_sold": true,
"invoice_schema_map": {
"classification_identifier_cpv": "79411000-8",
"peppol_billing_unit_code": "HUR"
},
"ean_number": null,
"upc_number": null,
"stripe_product_id_list": [
"prod_XXXXXXXX"
]
}'const baseUrl = "https://api.enlivy.com" ;
const token = process.env.ENLIVY_API_TOKEN;
const organizationId = "<organization_id>";
const result = await fetch(`${baseUrl}/organizations/${organizationId}/products` , {
method: "POST",
headers: {
Authorization: `Bearer ${token}`,
Accept: "application/json" ,
"Content-Type": "application/json" ,
},
body: JSON.stringify({
// required: digital | physical | service | bonus
type: "service",
// required: a { currency: amount } map (amounts max 2 decimals).
// With 2+ currencies, primary_currency becomes required.
price_map: {
EUR: "49.90",
USD: "54.00",
},
primary_currency: "EUR",
// per-locale maps (not plain strings)
name_lang_map: {
en: "Consulting Hour",
ro: "Oră de consultanță",
},
// the Peppol unit code, 3 characters: HUR for an hour, H87 for a piece
unit_code: "HUR",
description_lang_map: { en: "One hour of advisory services." },
// optional; unique per organization (alpha-dash)
alias: "consulting-hour",
organization_tax_class_id: "<tax_class_id>",
// whether it is actively offered
is_sold: true,
// optional e-invoicing metadata (used when the product lands on an invoice line)
invoice_schema_map: {
// CPV code
classification_identifier_cpv: "79411000-8",
// PEPPOL unit code
peppol_billing_unit_code: "HUR",
},
// optional identifiers
ean_number: null,
upc_number: null,
stripe_product_id_list: [
// each id must start with "prod_"
"prod_XXXXXXXX",
],
}),
}).then((response) => response.json());Update any part of a product: reprice it, relabel it, change its tax class, or toggle whether it is sold. Send only the fields you want to change.
/organizations/<organization_id>/products/<product_id> curl -X PUT 'https://api.enlivy.com/organizations/<organization_id>/products/<product_id>' \
-H "Authorization: Bearer $ENLIVY_API_TOKEN" \
-H 'Accept: application/json' \
-H 'Content-Type: application/json' \
-d '{
"price_map": {
"EUR": "59.90",
"USD": "64.00"
},
"primary_currency": "EUR",
"name_lang_map": {
"en": "Consulting Hour, Senior"
},
"is_sold": false
}'const baseUrl = "https://api.enlivy.com" ;
const token = process.env.ENLIVY_API_TOKEN;
const organizationId = "<organization_id>";
const productId = "<product_id>";
const result = await fetch(`${baseUrl}/organizations/${organizationId}/products/${productId}` , {
method: "PUT",
headers: {
Authorization: `Bearer ${token}`,
Accept: "application/json" ,
"Content-Type": "application/json" ,
},
body: JSON.stringify({
// reprice: the price_map replaces the stored one
price_map: {
EUR: "59.90",
USD: "64.00",
},
primary_currency: "EUR",
name_lang_map: { en: "Consulting Hour, Senior" },
is_sold: false,
}),
}).then((response) => response.json());Soft-delete a product so it is no longer offered. It can be restored later.
/organizations/<organization_id>/products/<product_id> # soft delete; restore via POST /products/restore/{productId}
curl -X DELETE 'https://api.enlivy.com/organizations/<organization_id>/products/<product_id>' \
-H "Authorization: Bearer $ENLIVY_API_TOKEN" \
-H 'Accept: application/json' const baseUrl = "https://api.enlivy.com" ;
const token = process.env.ENLIVY_API_TOKEN;
const organizationId = "<organization_id>";
const productId = "<product_id>";
// soft delete; restore via POST /products/restore/{productId}
const result = await fetch(`${baseUrl}/organizations/${organizationId}/products/${productId}` , {
method: "DELETE",
headers: {
Authorization: `Bearer ${token}`,
Accept: "application/json" ,
},
}).then((response) => response.json());API Reference
Authentication Sessions from the API
Create and retrieve customer-portal authentication sessions for your users, straight from the admin API. A session grants one of your organization’s users scoped, time-boxed access to the customer application (the portal), authenticated by email, phone, or a magic link. Find the user, open a session (by id or by email), then read it back.
Retrieve the users you can open a session for. Filter by email, run a free-text search, or page through the full list to find the right person.
/organizations/<organization_id>/users # filters: email, created_at_from/to, updated_at_from/to, ids, or q=... for free-text search
curl -G 'https://api.enlivy.com/organizations/<organization_id>/users' \
--data-urlencode 'page=1' \
--data-urlencode 'limit=20' \
--data-urlencode 'email=jane@acme.example' \
-H "Authorization: Bearer $ENLIVY_API_TOKEN" \
-H 'Accept: application/json' const baseUrl = "https://api.enlivy.com" ;
const token = process.env.ENLIVY_API_TOKEN;
const organizationId = "<organization_id>";
// filters: email, created_at_from/to, updated_at_from/to, ids, or q=... for free-text search
const query = new URLSearchParams({
page: "1",
limit: "20",
email: "jane@acme.example",
});
const result = await fetch(`${baseUrl}/organizations/${organizationId}/users?${query}` , {
method: "GET",
headers: {
Authorization: `Bearer ${token}`,
Accept: "application/json" ,
},
}).then((response) => response.json());Open a session for a specific user by referencing their organization_user_id. This targets exactly one identity, so the session proceeds straight to authentication with no ambiguity.
/organizations/<organization_id>/user-client-portal-sessions curl -X POST 'https://api.enlivy.com/organizations/<organization_id>/user-client-portal-sessions' \
-H "Authorization: Bearer $ENLIVY_API_TOKEN" \
-H 'Accept: application/json' \
-H 'Content-Type: application/json' \
-d '{
"organization_user_id": "<org_user_id>",
"name": "Jane Doe",
"authentication_method": "email",
"permissions": [
"invoices",
"receipts",
"payment_methods"
],
"validity_hours": 72
}'
# Response includes: token, status, magic_authentication_url (for magic_authentication),
# authentication_verification_code (for email / phone), permissions, expires_at. const baseUrl = "https://api.enlivy.com" ;
const token = process.env.ENLIVY_API_TOKEN;
const organizationId = "<organization_id>";
const result = await fetch(`${baseUrl}/organizations/${organizationId}/user-client-portal-sessions` , {
method: "POST",
headers: {
Authorization: `Bearer ${token}`,
Accept: "application/json" ,
"Content-Type": "application/json" ,
},
body: JSON.stringify({
// reference the exact user identity
organization_user_id: "<org_user_id>",
// required: display name for the session
name: "Jane Doe",
// how the user proves identity: email | phone | magic_authentication
authentication_method: "email",
// scope what the session may access in the portal
// options: invoices | receipts | network_exchanges | contracts | reports | payment_methods
permissions: [
"invoices",
"receipts",
"payment_methods",
],
// lifetime: set validity_hours OR expires_at (bounded by the server maximum)
validity_hours: 72,
}),
}).then((response) => response.json());
// Response includes: token, status, magic_authentication_url (for magic_authentication),
// authentication_verification_code (for email / phone), permissions, expires_at. Open a session by email instead of a specific id, handy when you only know the address. If the email maps to a single user, the session proceeds directly. If the same email belongs to multiple identities, the session is created in the awaiting_user_selection state and the user is prompted in the portal to choose which identity to authenticate as before continuing.
/organizations/<organization_id>/user-client-portal-sessions curl -X POST 'https://api.enlivy.com/organizations/<organization_id>/user-client-portal-sessions' \
-H "Authorization: Bearer $ENLIVY_API_TOKEN" \
-H 'Accept: application/json' \
-H 'Content-Type: application/json' \
-d '{
"email": "jane@acme.example",
"name": "Jane Doe",
"authentication_method": "email",
"permissions": [
"invoices",
"receipts",
"payment_methods"
],
"validity_hours": 72
}'
# Single match -> the session proceeds to authentication.
# Multiple matches -> status "awaiting_user_selection"; the user picks which
# identity to authenticate as in the portal before continuing.const baseUrl = "https://api.enlivy.com" ;
const token = process.env.ENLIVY_API_TOKEN;
const organizationId = "<organization_id>";
const result = await fetch(`${baseUrl}/organizations/${organizationId}/user-client-portal-sessions` , {
method: "POST",
headers: {
Authorization: `Bearer ${token}`,
Accept: "application/json" ,
"Content-Type": "application/json" ,
},
body: JSON.stringify({
// reference the user by email
email: "jane@acme.example",
// required: display name for the session
name: "Jane Doe",
// how the user proves identity: email | phone | magic_authentication
authentication_method: "email",
// scope what the session may access in the portal
// options: invoices | receipts | network_exchanges | contracts | reports | payment_methods
permissions: [
"invoices",
"receipts",
"payment_methods",
],
// lifetime: set validity_hours OR expires_at (bounded by the server maximum)
validity_hours: 72,
}),
}).then((response) => response.json());
// Single match -> the session proceeds to authentication.
// Multiple matches -> status "awaiting_user_selection"; the user picks which
// identity to authenticate as in the portal before continuing. Read a session back: its current status, when it was last used, when it expires, and the user it belongs to. Poll the status to see the user move through authentication (including identity selection when the session was opened by email).
/organizations/<organization_id>/user-client-portal-sessions/<user_client_portal_session_id> # includes: organization_user, organization
curl -G 'https://api.enlivy.com/organizations/<organization_id>/user-client-portal-sessions/<user_client_portal_session_id>' \
--data-urlencode 'include=organization_user' \
-H "Authorization: Bearer $ENLIVY_API_TOKEN" \
-H 'Accept: application/json'
# status: pending | email_verification_sent | awaiting_user_selection | success | expiredconst baseUrl = "https://api.enlivy.com" ;
const token = process.env.ENLIVY_API_TOKEN;
const organizationId = "<organization_id>";
const userClientPortalSessionId = "<user_client_portal_session_id>";
// includes: organization_user, organization
const query = new URLSearchParams({
include: "organization_user",
});
const result = await fetch(`${baseUrl}/organizations/${organizationId}/user-client-portal-sessions/${userClientPortalSessionId}?${query}` , {
method: "GET",
headers: {
Authorization: `Bearer ${token}`,
Accept: "application/json" ,
},
}).then((response) => response.json());
// status: pending | email_verification_sent | awaiting_user_selection | success | expired Works together with
- Bank Account Data See every connected account, balance, and transaction in one dashboard. Synced securely, kept current, and linked to the rest of your records.
- Bank Transactions Bring every transaction into Enlivy: synced from your bank, imported from a file, or entered by hand. Then tie each one to the invoice, receipt, or payslip it belongs to, so every payment sits next to the document it settles.
- Billing Packages Build a plan once and offer it weekly, monthly, yearly, or anything in between. Customers subscribe and manage their card from a branded portal, and switch cycle, change tier, pause, or cancel when you allow it. With the Billing Schedules pack, each cycle is invoiced for you and card payments are collected through your own Stripe account.
- Billing Schedules Set up a subscription, retainer, or payment plan, link its invoices and receipts, and see exactly how much has been paid, what is pending, and where each relationship stands.
- Contracts Draft contracts from templates, send them for signature with SMS or email identity verification, and track every signature and renewal.
- Customer Portal A branded, self-service portal, on your own domain if you add one, where clients accept proposals, pay invoices, manage subscriptions and sign contracts themselves. Every action lands on the client's record in Enlivy.
- Data Export Export your invoices, receipts, payslips, contracts, and transactions in the format and folder structure your accountant expects, for any date range. Built in, at no extra cost, on every plan.
- Dashboard See the work and numbers that matter to you, then customize the dashboard by adding and arranging available widgets.
- Document Intake Start with a document, choose where it belongs and review the form; an AI reading you request can suggest its details.
- Employment Records Who was employed, on what terms, in which country, what they did on each day, and how long every one of those has to be kept. Enlivy holds the employment record as a record, with the rules coming from the person's own jurisdiction rather than your company address.
- Guidelines Document your best practices and policies, and keep your team aligned with clear, accessible guidelines.
- Helpdesk Work through questions from your website widget and an IMAP-connected mailbox, assign a teammate, reply with context and hold mail from unknown senders for review.
- Invoices Create an invoice, send it to your client, and connect the contract, receipt or bank transaction that belongs with it. For Romanian e-Factura, use the configured ANAF connection.
- MCP Connect Enlivy to Claude, ChatGPT, Cursor, or any AI assistant through MCP. Ask about your invoices, contracts, and pipeline, and get real work done, safely scoped to exactly what your own account can already do.
- Network Exchanges For Romanian organizations, connect ANAF eFactura to receive invoices and submit the ones you issue to Romanian clients, manually or on a configured schedule. See the exchange status in Enlivy. For other markets, download a supported electronic invoice file for review and separate submission.
- Payslips Your accountant works out the figures. Enlivy is where they land: every amount on its own coded line, in the vocabulary your country actually uses, checked against the payslip's own totals and tied to the employment, the contract and the payment it belongs to.
- Playbooks Design, execute, and refine step-by-step procedures for any workflow. Ensure consistency, reduce errors, and guide your team through repeatable tasks with built-in structure and clarity.
- Products Build a reusable catalog of everything you sell, services or goods, with pricing, units, tax settings, and descriptions set once. Every product is ready to drop into a quote or invoice in seconds.
- Sales Pipeline See which opportunities are moving forward, review the conversation before you follow up and keep proposals close to the deal.
- Receipts Record money in and money out, keep supporting files together, and connect a receipt to its related invoice or contract when needed.
- Reports Replace updates spread across chat and email with structured reports your team fills in. You define the questions and the expected rhythm once with a reusable schema, share it with the people who answer it, and every submission lands in Enlivy, ready to compare.
- Slack Integration Stop checking dashboards to find out what changed. Enlivy brings important moments directly into Slack, from payments and proposals to Helpdesk conversations. Connect once, choose your events and route them to the right channels.
- Taxes Stop picking VAT rates by hand. Assign a tax class to a product or invoice line and Enlivy resolves the correct rate automatically, based on who the customer is and where they are, with the right EU codes for e-invoicing built in.
- Tasks Keep team work beside the invoices, prospects and projects it concerns. Tasks are included for every organization at no extra charge, with no separate Tasks subscription or task-count quota.
- Users Easily customize roles and permissions in Enlivy for clients, partners, accountants, and administrators, ensuring secure and efficient access.
- Webhooks Moments after something happens in Enlivy (an invoice is paid, a transaction lands, a user changes), a webhook fires to your system. React to events instead of polling for changes or waiting on reports.