User guide

Software Asset Management

Looking for what it does rather than how to use it? Read the Software licences overview .

What it is

Software Asset Management is the record of every software title your organization is entitled to run, who or what is using each seat, and what is actually installed on your hardware. Comparing those three things is what tells you whether you are over-paying, under-licensed, or about to lose access at a renewal you forgot.


Concepts

ConceptWhat it is
Software vendorThe publisher (Microsoft, Adobe). Carries support contacts, a documentation URL and an admin-console URL template.
Software productA catalogue entry for one title, optionally with version and edition. Private to your account, or in the shared library.
Software libraryThe platform-wide catalogue of approved products and vendors, readable by every tenant.
Software licenceA purchased entitlement held by one organization: model, seat count, dates, cost, keys.
Seat assignmentOne seat of a licence consumed by a person or by an asset.
Pool checkoutFor concurrent (floating) licences: one live slot, held by an agent and released on check-in or expiry.
InstallationAn observed copy of a product on an asset. Recorded manually or discovered by an RMM.
Compliance positionSeats held vs seats assigned vs copies installed, evaluated per product per organization.

Licensing models

PER_SEAT, PER_DEVICE, SITE, VOLUME, CONCURRENT, SUBSCRIPTION, PERPETUAL, OPEN_SOURCE, FREEMIUM, METERED, OTHER.

Only PER_SEAT and PER_DEVICE are evaluated for seat compliance. CONCURRENT is evaluated against its pool. Every other model is reported as compliant — there is no seat count to compare against.

Licence statuses

ACTIVE, EXPIRED, SUSPENDED, CANCELLED, PENDING_ACTIVATION, PENDING_RENEWAL, TRIAL, GRACE_PERIOD.

Compliance reads ACTIVE, PENDING_RENEWAL, GRACE_PERIOD and TRIAL. The over- and under-licensed reports narrow further to ACTIVE and PENDING_RENEWAL.


Roles and permissions

Every software mutation requires organization administrator or above. Reads are limited by tenant scope only, so a CUSTOMER can see their own organization’s licences but change nothing.

ActionCUSTOMERpower userorganization administratorMSP technicianMSP administratorplatform administrator
View licences, products, installations, compliance
Create / edit / delete a licence
Assign and revoke seats
Create / edit / delete a product or vendor (own account)
Record, edit or remove an installation
Run product / vendor enrichment
Submit a product to the shared library (promote)
Concurrent-pool checkout / check-in / cleanup
Approve or reject a product into the shared library
Publish a vendor into the shared library (isPublic)
Mark a vendor verified
Read the platform library stats and moderation queue

MSP technician ranks above organization administrator in Solidlio’s hierarchy, so a floor set one notch too high locks the whole organization portal out. The floor here is organization administrator everywhere, and the web UI gates on exactly the same line (useCanManageSoftware), not on isStaff — which is MSP-only and would refuse the org admins the API accepts.

Tenant scoping is separate from role. A platform admin is unscoped; a customer sees only their own organization; MSP and org staff see every organization inside their account. A request with no tenant context matches no rows.

Library visibility

A product or vendor you create belongs to your account and is invisible to every other tenant. It enters the shared library only through moderation:

PRIVATE  --promote-->  PENDING  --approve-->  PUBLIC
                              \--reject--->  REJECTED

Vendors have no promote step; a platform administrator flips isPublic directly. Neither isPublic nor isVerified can be set by a tenant — they are platform trust signals shown to everyone.


Walkthroughs

Record a licence you have bought

  1. Go to Software → Products and confirm the title exists. If it does not, choose Add Software, pick the vendor, category and type. New products start PRIVATE to your account.
  2. Go to Software and choose + Add License, or Software → Licenses → New.
  3. Pick the product and the owning organization, then the licensing model.
  4. Record the seat count (totalSeats), the term dates and the cost. Leave totalSeats empty for an unlimited licence.
  5. Record the identifiers you will need later: licence key, serial number, agreement number, subscription ID, vendor account ID. They appear on the licence’s Identifiers & Access panel afterwards, with the key masked until you choose Reveal.
  6. Save. The licence detail page opens.

Assign and reclaim seats

  1. Open the licence and choose Manage Seats.
  2. Choose User, Contact or Asset, pick the holder, optionally set an expiry, and choose Assign. Anyone already holding a seat is filtered out of the picker.
  3. The seat counter increments immediately. When the licence is full the API refuses further assignments with “No available seats for this license” — the seat claim is atomic, so two simultaneous assignments cannot oversubscribe a licence.
  4. To reclaim a seat, choose Revoke on its row and confirm. The assignment moves to REVOKED, the reason is stored, and the seat is immediately available again.

Work the compliance dashboard

  1. Go to Software → Compliance. The score is the share of evaluated licences that are compliant.
  2. Over-Licensed lists licences with more than 30% of their seats unused, with the annual value of the waste. A licence with no recorded cost shows “cost not recorded” — never $0.
  3. Under-Licensed lists licences where assigned seats or installed copies exceed the seats held, with the shortfall, a severity, and the estimated cost to true up. Choose Add License to buy the gap.
  4. Expiring lists everything due within 90 days, bucketed CRITICAL (≤7 days), HIGH (≤14), MEDIUM (≤30), LOW beyond that.
  5. Unlicensed lists discovered installations with no licence behind them. Where you already hold a licence for that product in that organization, an Attach a licence… picker appears on the row; choosing one links the install and it leaves the report. Where you hold none, the row offers Add License instead.
  6. Overview is the per-product, per-organization position: seats held, copies installed, compliance level and variance.

Track renewals

  1. Go to Renewals in the sidebar. Choose a 30, 60 or 90-day window.
  2. Renewals are grouped Next 7 days, Next 30 days and Later, showing seats, expiry date, whether auto-renew is on, and the recurring cost.
  3. Needs action counts the renewals with auto-renew off.
  4. Independently of this page, the daily asset-expiry-notifications job creates an in-app LICENSE_EXPIRING notification for every admin, manager and technician in the licence’s organization when the days-to-expiry lands exactly on one of the licence’s renewalReminderDays values (default [30, 14, 7]). The notification links to that licence in the recipient’s own portal, and lastReminderSent suppresses a repeat within 24 hours.

Manage a concurrent (floating) licence

  1. Create the licence with model CONCURRENT and a maxConcurrent limit.
  2. The licence detail page shows a Concurrent Use panel: slots in use, slots available, and how many checkouts expired without being returned.
  3. Agents call the pool API to check a slot out and heartbeat it. Capacity is enforced by locking the licence row and counting live checkouts, so the pool cannot be oversubscribed.
  4. Choose Check in on a row to release a slot by hand, or Release N expired to reclaim every checkout whose expiry has passed.

Submit a product to the shared library (platform administrators)

  1. A tenant opens their product and chooses Promote to Library (PRIVATE → PENDING).
  2. A platform administrator opens Platform → Software Library, works the Pending Approvals queue, and chooses Approve or Reject. A rejection requires a reason.
  3. Approval records who approved it and when, and clears any prior rejection. Rejection does the reverse, so a row never carries both decisions at once.
  4. Platform → Software Library → Vendors lists every public vendor. Choose the Verify badge on a row to toggle the platform verification mark.

Configuration

SettingWhat it doesIf unset
totalSeatsThe entitlement compliance measures against.Licence is unlimited; compliance reports it as UNKNOWN.
usedSeatsMaintained by the service on assign/revoke. Not editable directly.
maxConcurrentSlot ceiling for a CONCURRENT licence.Pool is unbounded; checkouts never refuse.
expirationDateDrives the expiring report, the renewals dashboard and the reminder job.Licence never appears in any expiry view.
renewalDateShown on the licence and the expiring report. Informational.Shown as -.
renewalReminderDaysDays-before-expiry on which the reminder job fires.Defaults to [30, 14, 7] at creation.
lastReminderSentSet by the job. Suppresses a repeat within 24 hours.First matching threshold notifies.
autoRenewReported on renewals and in the reminder message.Treated as manual renewal.
recurringCost + billingCycleAnnualised to price wasted spend and total annual spend.Waste and exposure report null — “cannot price”, not $0.
purchaseCostSecond-choice per-seat basis for risk exposure.Falls through to the product MSRP.
Product msrpPriceLast-resort per-seat basis for risk exposure.The licence counts as unpriced.
licenseKey and friendsDisplayed on the licence’s Identifiers & Access panel; key masked until revealed.Panel omits the field.
consoleUrlLicence-level override for the vendor admin console link.Falls back to the product’s, then the vendor’s URL template.
contractIdLinks the licence to a billing contract.Licence stands alone.

How compliance is computed

Read this before you argue with a number.

  • Installations are counted per product per organization — never across an MSP’s whole account. One client’s copies can never make another client look over-deployed.
  • Installed copies are compared against every seat that organization holds for that product, summed across all of its licences for the title. Two licences for one product add up; they are not each compared to the whole install count.
  • Over-deployment is reported once per (product, organization) — on the licence with the most seats — so both the row count and the dollar exposure are reported once, not once per licence.
  • Over-licensed means more than 30% of seats are unused.
  • Under-licensed means assigned seats exceed seats held, or installed copies exceed the organization’s total seats for that product.
  • Seats are priced from the licence’s own recurring cost annualised by its own billing cycle; failing that, its purchase cost; failing that, the product’s MSRP. Nothing is ever guessed. A figure that cannot be priced is reported as null, with costCoverage telling you how many licences were priced and how many were not.
  • Wasted spend uses only the recurring cost — money you actually pay. A vendor MSRP is not your spend, so it is not used to price waste (it is used to price risk, where it is a fair proxy for a true-up).

Plan tiers

Software Asset Management carries no plan gate. Every tier gets licences, products, vendors, assignments, pools, installations, compliance reporting and renewals in full. There is no cap on licences, products or seats.

It does not apply to software records.


Troubleshooting

These are the actual messages the service returns.

MessageCause
No available seats for this licenseEvery seat is assigned. Revoke one or raise totalSeats.
Not enough available seats for this licenseA bulk assignment larger than the free seats. It is refused in full.
This license is already assigned to this person/assetDuplicate seat assignment.
Assignment is not activeRevoke or update was called on an already-revoked seat.
Cannot update non-active assignmentExpiry can only be changed while the assignment is ACTIVE.
Cannot delete license with active assignmentsRevoke the seats first.
Cannot delete product with associated licensesDelete or re-point the licences first.
Cannot delete product with associated installationsRemove the installation records first.
Cannot delete vendor with associated productsSame, one level up.
Only a platform administrator can change a vendor’s library visibility or verificationisPublic / isVerified were sent by a non-platform caller.
Only PRIVATE products can be promotedThe product is already pending, public or rejected.
Only PENDING products can be approved / rejectedModeration acts on the pending queue only.
Product is already approved / already rejectedThe library route was called on a settled product.
A vendor with this name already existsVendor names are unique per account.
Vendor has no website URL for enrichmentSet the vendor website before enriching.
This license does not support concurrent checkoutsPool checkout used on a non-CONCURRENT licence.
License is not activePool checkout on a licence that is not ACTIVE.
No available concurrent slotsmaxConcurrent reached. Check something in or run cleanup.
Checkout has expired (410)Heartbeat arrived after the checkout expired.
Must provide checkoutId or personIdPool check-in needs one of the two.
Installation already exists for this product/asset combinationOne installation row per (product, asset).
License not found (400, on an installation update)The licence is outside your tenant, or belongs to a different organization than the asset.
Person not found / Asset not found (400)The assignment target is outside your tenant.
Organization not found (400, on licence create)Cross-account licence creation. The destination must be inside your account.
Account context required to create a vendor / a product (403)The request carried no account context.

Limits and known behaviour

Stated plainly so you do not plan around them.

  • The schema comment that claimed pgcrypto did this was wrong — pgcrypto is not installed anywhere; the encryption is done by the application.
    • This is encryption at rest, not an access control. Every read path decrypts before responding, so anyone who can already read the licence through the API — including a CUSTOMER in that organization — still sees the key. The Reveal toggle in the UI remains a display convenience.
    • Rows created before the change are still plaintext. The ciphertext envelope is self-identifying, so a plaintext key is detected and returned unchanged rather than throwing. --dry-run reports without writing.
    • licenseKey can never be searched, filtered or sorted on. The ciphertext is randomised per write, so two rows with the same key look different. The licence search box matches serialNumber, agreementNumber and product name — those three remain plaintext for exactly that reason.
    • Running the backfill under a different key than the services makes every key unreadable.
  • Concurrent pool checkout, check-in and heartbeat are agent APIs. The licence page shows live pool state and offers manual check-in and expired-slot cleanup, but there is no screen that checks a slot out — that is the job of the software holding the licence.
  • It duplicates what SoftwareLicense already records (cost, billing cycle, commitment, renewal date, quantity) and records nothing SoftwareLicense lacks. The router, the api-client module and the hooks are now marked @deprecated.
    • The table and its routes are retained, not dropped. Rows may already exist and must stay readable and editable through the API.
    • No UI will be built for it. Model new software spend as a SoftwareLicense.
  • usedQuantity, status, purchaseDate and consoleUrl on OrganizationSoftwareAsset are never written by its own routes.
  • Compliance evaluates only PER_SEAT and PER_DEVICE. SITE, VOLUME, SUBSCRIPTION, PERPETUAL, METERED and the rest are reported compliant because there is no seat count to test. CONCURRENT is likewise reported compliant in the summary — its real position is the pool panel, which is not folded into the compliance score.
  • A licence with totalSeats unset is UNKNOWN, not compliant. It counts towards unknown and lowers the compliance score, because the score’s denominator is every evaluated licence.
  • Seat assignment does not check the assignee’s organization. It checks the person or asset is inside your account. An MSP can therefore assign a seat on Client A’s licence to a person in Client B, both being inside the MSP’s account.
  • There is no licence renewal action. Renewals are a reporting view and a notification; extending a term means editing expirationDate by hand. autoRenew is recorded and reported but nothing acts on it.
  • Software changes are not written to the audit log. Licence, assignment, product and vendor changes are recorded only as the resulting row state. Library approval and rejection are the exception — they store who and when.
  • Installations are the only compliance input. If nothing populates installations (no RMM integration, no manual entry), under-licensing can only be detected from assigned seats.
  • The mirror stays — roughly twenty catalog/commerce tests depend on it.
  • RMM-discovered vendors and products are private to the discovering account. They do not enter the shared library, so two tenants running the same title each get their own catalogue row.

Questions this guide did not answer?

Ask us. You will get a reply from someone who uses the product every day.

Book a demo Contact us

A 30-minute walkthrough against your own workflow. No slides.