> For the complete documentation index, see [llms.txt](https://docs.younium.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.younium.com/crm-integrations/planhat.md).

# Planhat

## Overview

The Planhat integration provides **one-way export** from Younium to Planhat for customer success and revenue operations. Younium pushes accounts, subscription charges, and invoices into Planhat as companies, licenses (or sales records), and invoices. Planhat does not write data back to Younium through this integration.

Authentication uses a Planhat API bearer token and tenant-specific API host. The integration runs at the legal entity level and stores Planhat record identifiers on Younium integration custom fields to support create-and-update behaviour across scheduled jobs.

***

## Authentication

Planhat does not offer an approval flow to click through, so you connect it by pasting in a token. In Planhat, generate an API token; in Younium, enter that token together with your Planhat host name on the integration settings and save.

The host name must be a Planhat host — `planhat.com` or a regional host under it, such as `api.planhat.com` or `api-eu3.planhat.com`. Planhat demo workspaces on `planhatdemo.com` are also accepted. Younium always connects over HTTPS, and a leading `http://` or `https://` is removed if you paste a full address. Any other host is refused with a message that the endpoint must be a `planhat.com` host, and nothing is stored.

Younium immediately tests the token against your Planhat tenant. If it works, the connection is marked valid, the Younium custom fields the integration needs are created, and the settings are saved. If it does not, nothing is stored and Younium tells you the token or the host name is wrong — the two failures look the same from outside, so check both.

The connection is made per legal entity and stays valid until the token is revoked in Planhat or replaced in Younium.

> **Existing connections:** Younium checks the stored host name before every request to Planhat. If a connection saved earlier uses a host that is not a Planhat host, exports stop with a message asking you to re-authenticate the integration with a valid host name. Planhat redirects are not followed.

### Settings

| Setting                                                              | Description                                                            | Default               |
| -------------------------------------------------------------------- | ---------------------------------------------------------------------- | --------------------- |
| `OnlyLastVersionCharges`                                             | Subscription export uses last-version charge rules only                | Configurable on save  |
| `SyncToInvoiceAccount`                                               | Match accounts and charges by invoice account instead of order account | Configurable on save  |
| `LastAccountExport` / `LastSubscriptionExport` / `LastInvoiceExport` | When each export last completed successfully                           | Empty until first run |

***

## Activation

Saving a token that validates is what activates the integration: Younium creates the custom fields it needs and begins exporting on the schedule you configure. Younium does not register anything inside Planhat when it connects — no webhooks, no subscriptions — so Planhat has no record of the connection beyond the token you issued.

***

## Deactivation

Disconnecting removes the token, the host name and the record of when each export last ran. Exports stop.

The Younium custom fields holding Planhat identifiers stay on your records, keeping the history of what was exported, but nothing maintains them any longer. Data already in Planhat is untouched.

***

## Save and Setup

`SaveSettings` updates:

| Setting                  | Description                                                                   |
| ------------------------ | ----------------------------------------------------------------------------- |
| `OnlyLastVersionCharges` | Changes subscription export selection and update matching (see Eligibility)   |
| `SyncToInvoiceAccount`   | Uses `InvoiceAccountId` instead of `AccountId` for company linkage on charges |

Saving does not re-validate the token or recreate custom fields unless authenticate runs again.

`RemoveLastSyncDate` clears `LastAccountExport`, `LastSubscriptionExport`, or `LastInvoiceExport` when `syncType` is `Accounts`, `Subscriptions`, or `Invoices`.

***

## Custom Fields Created by the Integration

Resolved names follow `integrationPlanhat` + description (spaces removed). Do not rename or delete.

| Entity               | Field description       | Resolved name                             | Purpose                                   |
| -------------------- | ----------------------- | ----------------------------------------- | ----------------------------------------- |
| Account              | Planhat Id              | `integrationPlanhatPlanhatId`             | Planhat company `_id`                     |
| Order product charge | Planhat License Id      | `integrationPlanhatPlanhatLicenseId`      | License or sale `_id`                     |
| Order product charge | Planhat License version | `integrationPlanhatPlanhatLicenseversion` | Charge version string for update matching |
| Invoice              | Planhat Invoice Id      | `integrationPlanhatPlanhatInvoiceId`      | Planhat invoice `_id`                     |

***

## Eligibility and Scope

### Accounts

| Rule             | Detail                                                                |
| ---------------- | --------------------------------------------------------------------- |
| Account type     | `AccountType.Customer` only                                           |
| Active           | `Inactive == false`                                                   |
| Create vs update | Update when `integrationPlanhatPlanhatId` has value; else POST create |

### Subscription charges

| Rule                     | Detail                                                                                                                                 |
| ------------------------ | -------------------------------------------------------------------------------------------------------------------------------------- |
| Order status             | Active or Cancelled                                                                                                                    |
| Order type               | Subscription                                                                                                                           |
| Order version            | `IsLastVersion` on order                                                                                                               |
| Template charges         | `AssociatedChargeTemplateId == null`                                                                                                   |
| Account link             | Account (or invoice account if `SyncToInvoiceAccount`) must have Planhat company Id                                                    |
| Incremental              | If `LastSubscriptionExport` set: order or charge `Modified` ≥ last export minus one day                                                |
| `OnlyLastVersionCharges` | When true, only `IsLastVersion` charges; recurring updates match by license Id; inserts set `externalId` = charge number               |
| Excluded charges         | `ChangeState == Credit`, or “dead” period (start > end) **without** existing Planhat license Id                                        |
| Milestone start          | Recurring: skip if start on milestone with no effective start and no planned milestone date; else use planned date                     |
| Milestone end            | Use planned end when milestone end without effective end; otherwise effective end (may be null)                                        |
| One-off                  | Exported to `/sales`; recurring to `/licenses`                                                                                         |
| Reverted orders          | Entity log action “Order reverted to previous version” may trigger DELETE of higher-version licenses in Planhat for same charge number |

### Invoices

| Rule           | Detail                                                                       |
| -------------- | ---------------------------------------------------------------------------- |
| Status         | Posted or Settled                                                            |
| Account        | Must have Planhat company Id                                                 |
| Lines          | At least one recurring line with service period start; one-off lines skipped |
| Service period | End date required on line for inclusion in `lineItems`                       |

### Jobs

| Job                             | Minimum repeat (minutes) |
| ------------------------------- | ------------------------ |
| Export accounts to Planhat      | 30                       |
| Export subscriptions to Planhat | 30                       |
| Export invoices to Planhat      | 30                       |

***

## Field Mapping — Account Export (Younium → Planhat)

POST `/companies` for inserts; PUT `/companies` batch for updates.

| Younium Field                   | Planhat field                        |
| ------------------------------- | ------------------------------------ |
| `Account.Name`                  | `name`                               |
| (fixed)                         | `status` = `customer`                |
| `Account.ExternalCRMId`         | `sourceId` (only when not null)      |
| `Account.InvoiceEmailAddress`   | `custom.Younium Invoice Email`       |
| `Account.OrganizationNumber`    | `custom.Younium Organization Nr`     |
| `Account.TaxRegistrationNumber` | `custom.Younium Tax Registration Nr` |
| `Account.LegalEntityId`         | `custom.YouniumLegalEntityId`        |
| `Account.Id`                    | `custom.YouniumAccountId`            |
| `integrationPlanhatPlanhatId`   | `_id` (updates only)                 |

Integration custom mappings (entity `account`) can map additional Younium properties to Planhat top-level or `custom_*` fields. Calculated field paths in mappings are rejected.

After POST create, Planhat `_id` is written to `integrationPlanhatPlanhatId` on the account.

***

## Field Mapping — Recurring Subscription Export (Younium → Planhat `/licenses`)

| Younium Field                                      | Planhat field                                                                 |
| -------------------------------------------------- | ----------------------------------------------------------------------------- |
| `OrderProductCharge.Name`                          | `product`                                                                     |
| Planhat company Id                                 | `companyId`                                                                   |
| `Order.Currency.Code`                              | `_currency`                                                                   |
| `OrderProductCharge.EffectiveEndDate` has value    | `fixedPeriod` (boolean)                                                       |
| `OrderProductCharge.RecurringMonthlyAmount.Amount` | `mrr`                                                                         |
| Auto-renew flag                                    | `autoRenews` — false when order evergreen **or** charge ends on specific date |
| `Order.RenewalTerm`                                | `renewalPeriod`                                                               |
| `Order.NoticePeriod`                               | `noticePeriod`                                                                |
| (fixed)                                            | `noticeUnit` = `month`                                                        |
| Start date resolution                              | `fromDate` — milestone planned date or `EffectiveStartDate`                   |
| End date resolution                                | `toDate` — milestone planned or `EffectiveEndDate`                            |
| `Order.OrderNumber`                                | `custom.Younium Order`                                                        |
| `OrderProductCharge.ChargeNumber`                  | `custom.YouniumChargeNr`                                                      |
| `OrderProductCharge.LegalEntityId`                 | `custom.YouniumLegalEntityId`                                                 |
| `OrderProductCharge.Model`                         | `custom.Younium Charge Model`                                                 |
| `OrderProductCharge.Version`                       | `custom.YouniumChargeVersion`                                                 |
| `Order.Status`                                     | `custom.YouniumStatus`                                                        |
| `OrderProductCharge.ChangeState`                   | `custom.YouniumOrderChargeStatus`                                             |
| `OrderProductCharge.PricePeriod`                   | `custom.Younium Price Period`                                                 |
| `OrderProductCharge.BillingPeriod`                 | `custom.Younium Billing Period`                                               |
| `OrderProductCharge.ChargeNumber`                  | `externalId` (when `OnlyLastVersionCharges`)                                  |
| `integrationPlanhatPlanhatLicenseId`               | `_id` (updates)                                                               |

### Renewal status (recurring, when not `OnlyLastVersionCharges`)

| Condition                                                                                      | Planhat `renewalStatus` |
| ---------------------------------------------------------------------------------------------- | ----------------------- |
| Latest version charge has end date on evergreen order, or order Cancelled, or active price < 0 | `lost`                  |
| Charge not last version and latest version has no end date                                     | `renewed`               |
| (default)                                                                                      | omitted (ongoing)       |

Update when license Id exists and (`OnlyLastVersionCharges` **or** stored license version equals charge version). Insert otherwise. After POST, Planhat `_id` and version stored on charge custom fields.

PUT `/licenses` batch for updates; POST per row for inserts.

***

## Field Mapping — One-Off Export (Younium → Planhat `/sales`)

| Younium Field                           | Planhat field                                                   |
| --------------------------------------- | --------------------------------------------------------------- |
| `OrderProductCharge.Name`               | `product`                                                       |
| Planhat company Id                      | `companyId`                                                     |
| `Order.Currency.Code`                   | `_currency`                                                     |
| `OrderProductCharge.EffectiveStartDate` | `salesDate`                                                     |
| `OrderProductCharge.TCV.Amount`         | `value`                                                         |
| Custom block                            | Same `custom.*` keys as recurring (except price/billing period) |
| `integrationPlanhatPlanhatLicenseId`    | `_id` (updates when version matches)                            |

***

## Field Mapping — Invoice Export (Younium → Planhat `/invoices`)

Dates in `lineItems` and header use Unix **days** (epoch seconds ÷ 86400).

| Younium Field                        | Planhat field                                |
| ------------------------------------ | -------------------------------------------- |
| `Invoice.DueDate`                    | `dueDate`                                    |
| `Invoice.InvoiceDate`                | `invoiceDate`                                |
| `Invoice.Currency.Code`              | `currency`                                   |
| Planhat company Id                   | `cId`                                        |
| `Account.Name`                       | `cName`                                      |
| `Invoice.Status`                     | `status` — `paid` if Settled, else `pending` |
| `Invoice.Subtotal.Amount`            | `amountDue`                                  |
| `Invoice.InvoiceNumber`              | `custom.Younium Invoice Number`              |
| `Invoice.Status`                     | `custom.Younium Invoice Status`              |
| `Invoice.Id`                         | `custom.YouniumInvoiceId`                    |
| `integrationPlanhatPlanhatInvoiceId` | `_id` (updates)                              |

### Invoice line item (`lineItems[]`, recurring only)

| Younium Field                               | Planhat field                                  |
| ------------------------------------------- | ---------------------------------------------- |
| `InvoiceLine.ChargeDescription`             | `product`                                      |
| `InvoiceLine.Subtotal.Amount`               | `amount`                                       |
| `InvoiceLine.ServicePeriodStartDate`        | `dateFrom` (Unix days)                         |
| `InvoiceLine.ServicePeriodEndDate`          | `dateTo` (Unix days; line skipped if end null) |
| Charge `integrationPlanhatPlanhatLicenseId` | `licenseId` (when present)                     |

Invoices with no qualifying line items are skipped entirely.

Integration custom mappings (entity `invoice`) may add fields to `custom` or top-level invoice payload.

***

## Custom Mapping Configuration

Younium loads **integration custom mappings** per entity (`account`, `orderproductcharge`, `invoice`). For each mapping:

* Younium resolves `YouniumFieldName` via field configuration `PropertyPath` (nested paths supported; calculated fields with `()` are rejected).
* Planhat field `IntegrationFieldName` starting with `custom_` writes into `custom` dictionary; otherwise top-level.
* For `orderproductcharge` mappings, the Younium field can be a custom field on the charge's **Order** rather than on the charge itself; for `invoice` mappings, it can be a custom field on the invoice's linked **Order**. If the charge has no order, or the invoice has no linked order, the mapping is skipped for that record rather than failing the export.

Property discovery for mapping UI: sample GET `/companies?limit=1`, `/licenses?limit=1`, `/invoices?limit=1`, plus `/customfields?parent={company|license|invoice}` — returned names include `custom_{name}` for custom fields.

***

## Export Accounts (batch job)

**Trigger:** **Export accounts to Planhat** (min repeat 30 minutes).

**Outcome:** PUT batch updates, per-row POST creates, `LastAccountExport` timestamp, integration success log with new/updated counts. Per-account POST failures are logged without stopping the whole job.

***

## Export Subscriptions (batch job)

**Trigger:** **Export subscriptions to Planhat** (min repeat 30 minutes).

**Outcome:** Updates and creates on `/licenses` and `/sales`, optional DELETE for reverted-order cleanup, `LastSubscriptionExport`, counts `NewSubscriptions` / `UpdatedSubscriptions`. Failures logged on batch errors or individual charge POST errors.

***

## Export Invoices (batch job)

**Trigger:** **Export invoices to Planhat** (min repeat 30 minutes).

**Outcome:** PUT batch updates, per-row POST creates with Id write-back, `LastInvoiceExport`, new/updated counts. Missing Planhat account logs failure for that invoice but processing continues for others.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.younium.com/crm-integrations/planhat.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
