API2 always returns 401 Unauthorized with valid Access Tokens

I created multiple Access Tokens from:

Settings β†’ Access Tokens

I copied each token immediately after creation and tested it with both curl and my own extension.

Example:

curl -v -H "X-API-KEY: <my_access_token>" http://localhost:8080/api2/businesses

The request is sent correctly, and I can confirm that the X-API-KEY header is included.

However, every API endpoint returns:

HTTP/1.1 401 Unauthorized

{
  "error": "Unauthorized"
}

I tested:

  • GET /api2/businesses
  • GET /api2/access-tokens
  • GET /api2/version

All return 401 Unauthorized.

Things I have already verified:

  • Running the official Manager Server executable.
  • API documentation is available at /api2, so the API is enabled.
  • Created multiple new Access Tokens.
  • Tested multiple businesses.
  • Tested from curl.
  • Tested from a browser extension.
  • Tested with different browsers.
  • Restarted the server after creating new tokens.

The request always reaches the server, but the server rejects every Access Token with HTTP 401.

My server is started with:

ManagerServer.exe --urls http://localhost:8080 --path "C:\Users\Administrator\Documents\manager"

Is there any known issue with API2 authentication in version 26.7.23.3657, or is there any additional configuration required for Access Tokens?

Thank you.

I run a POS that talks to api2 on the desktop edition every day, same build as yours actually (26.7.23.3657), and X-API-KEY works fine here. So it’s not a version-wide break.

Two things worth checking. First, GET /api2 with no token at all should return the OpenAPI page. If that works, the server itself is fine and the problem is only the token lookup.

Second, access tokens are per-business, they live inside the business data itself. You start the server with --path pointing to C:\Users\Administrator\Documents\manager. If the tokens were created while Manager was open on a different data folder, the server you’re hitting has never heard of them, and 401 with valid-looking tokens is exactly what that would look like. I’d create one fresh token from the server’s own UI and try that before anything else.

import sys

import os

import csv

import io

import json

import threading

import webbrowser

import requests

import webview

from datetime import datetime

from flask import Flask, render_template_string, request, jsonify, Response, send_file

from num2words import num2words

# ReportLab for PDF receipt generation

from reportlab.lib.pagesizes import letter

from reportlab.platypus import SimpleDocTemplate, Paragraph, Spacer, Table, TableStyle

from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle

from reportlab.lib import colors

app = Flask(_name_)

# ==========================================

# COMPANY & MANAGER.IO API CONFIGURATION

# ==========================================

COMPANY_NAME = β€œSVL (Pvt) Ltd.”

BUSINESS_KEY = β€œogYOU1ZMIChQdnQpIEx0ZC4”

MANAGER_URL = β€œhttps://mydomainname.manager.io”

API_KEY = β€œmy access token”

TRANSACTIONS = []

def parse_amount(val):

if isinstance(val, dict):

    for sub_k in \['number', 'value', 'amount'\]:

        if sub_k in val and val\[sub_k\] is not None:

            return parse_amount(val\[sub_k\])

    return 0.0

try:

    return float(val)

except (ValueError, TypeError):

    return 0.0

def amount_to_words(amount):

try:

    val = parse_amount(amount)

    rupees = int(val)

    cents = int(round((val - rupees) \* 100))

    words = num2words(rupees, lang='en').title().replace("-", " ")

    if cents > 0:

        return f"\*\*\*{words} Rupees and {cents:02d}/100 Only\*\*\*"

    else:

        return f"\*\*\*{words} Rupees Only\*\*\*"

except Exception:

    return ""

def get_auth_session():

"""Configures requests session for Manager.io Cloud API."""

session = requests.Session()

session.auth = (API_KEY, '')

session.headers.update({

    "Accept": "application/json",

    "Content-Type": "application/json"

})

return session

# ==========================================

# MANAGER.IO INVOICE & RECEIPT LINKING

# ==========================================

def fetch_invoices_from_manager(query=β€œβ€, start_dt=β€œβ€, end_dt=β€œβ€):

"""Fetches sales invoices from Manager.io to allow settlement at POS."""

session = get_auth_session()

url = f"{MANAGER_URL}/api/{BUSINESS_KEY}/sales-invoices"



try:

    res = session.get(url, timeout=10)

    if res.status_code != 200:

        return \[\], f"HTTP {res.status_code}: {res.text\[:100\]}"

        

    data = res.json()

    raw_invoices = data.get('salesInvoices', \[\]) if isinstance(data, dict) else data

    if not raw_invoices and isinstance(data, dict):

        raw_invoices = list(data.values())



    invoices = \[\]

    for inv in raw_invoices:

        if not isinstance(inv, dict):

            continue



        inv_key = inv.get('Key') or inv.get('key')

        inv_num = inv.get('Reference') or inv.get('reference') or inv.get('InvoiceNumber') or 'N/A'

        inv_date = str(inv.get('IssueDate') or inv.get('date') or inv.get('Date') or '')

        

        amt = parse_amount(inv.get('InvoiceAmount') or inv.get('Amount') or inv.get('amount'))

        

        customer = inv.get('Customer') or 'General Customer'

        if isinstance(customer, dict):

            customer = customer.get('Name') or customer.get('name') or 'General Customer'



        lines = inv.get('Lines', \[\])



        \# Safe String Filter Matching

        if query and (query.lower() not in str(inv_num).lower() and query.lower() not in str(customer).lower()):

            continue

        if start_dt and inv_date and inv_date < start_dt.split("T")\[0\]:

            continue

        if end_dt and inv_date and inv_date > end_dt.split("T")\[0\]:

            continue



        invoices.append({

            "key": inv_key,

            "invoice_number": inv_num,

            "date": inv_date,

            "customer": customer,

            "amount": amt,

            "lines": lines

        })



    return invoices, f"OK ({len(invoices)} Invoices)"

except Exception as e:

    return \[\], str(e)

def link_receipt_to_manager_invoice(invoice_key, pos_ref, amount, payment_method):

"""Creates a payment receipt in Manager.io linked directly to the Sales Invoice."""

session = get_auth_session()

url = f"{MANAGER_URL}/api/{BUSINESS_KEY}/receipts"



payload = {

    "Date": datetime.now().strftime("%Y-%m-%d"),

    "Reference": pos_ref,

    "Description": f"POS Settlement ({pos_ref}) - Method: {payment_method}",

    "Lines": \[

        {

            "SalesInvoice": invoice_key,

            "Amount": amount

        }

    \],

    "Amount": amount

}



try:

    res = session.post(url, json=payload, timeout=10)

    if res.status_code in (200, 201):

        res_data = res.json() if res.text else {}

        key = res_data.get('Key') or res_data.get('key') or pos_ref

        return True, key

    else:

        return False, f"HTTP {res.status_code}: {res.text\[:120\]}"

except Exception as e:

    return False, str(e)

# ==========================================

# UI TEMPLATES

# ==========================================

POS_TEMPLATE = β€œβ€"

<title>POS Settlement Terminal - {{ company_name }}</title>

<style>

    body { font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; background: #f4f6f9; margin: 0; padding: 20px; }

    .container { max-width: 1400px; margin: 0 auto; background: white; padding: 25px; border-radius: 8px; box-shadow: 0 4px 12px rgba(0,0,0,0.1); }

    .header-row { display: flex; justify-content: space-between; align-items: flex-start; margin-bottom: 20px; }

    h2 { color: #1e293b; margin: 0 0 5px 0; }

    .subtitle { color: #64748b; font-weight: 500; }

    

    .pos-grid { display: grid; grid-template-columns: 1fr 480px; gap: 20px; }

    .panel { background: #fff; border: 1px solid #e2e8f0; border-radius: 6px; padding: 15px; }



    .filter-panel { background: #f8fafc; border: 1px solid #cbd5e1; padding: 12px; border-radius: 6px; margin-bottom: 15px; display: flex; gap: 10px; align-items: flex-end; }

    .filter-group { display: flex; flex-direction: column; gap: 4px; flex: 1; }

    .filter-group label { font-size: 11px; font-weight: bold; color: #475569; }

    .filter-input { padding: 8px; border: 1px solid #cbd5e1; border-radius: 4px; font-size: 13px; }



    .invoice-table { width: 100%; border-collapse: collapse; margin-top: 10px; }

    .invoice-table th, .invoice-table td { padding: 10px; border-bottom: 1px solid #e2e8f0; text-align: left; font-size: 13px; }

    .invoice-table th { background: #f1f5f9; color: #475569; }

    .invoice-row { cursor: pointer; }

    .invoice-row:hover { background: #eff6ff; }

    .invoice-row.selected { background: #dbeafe; border-left: 4px solid #2563eb; }



    .totals-box { background: #f8fafc; padding: 12px; border-radius: 6px; margin-bottom: 15px; border: 1px solid #e2e8f0; }

    .total-row.grand { font-size: 18px; font-weight: bold; color: #1e293b; }



    .cash-box { background: #eff6ff; border: 1px solid #bfdbfe; padding: 12px; border-radius: 6px; margin-bottom: 15px; }

    .cash-box label { font-size: 12px; font-weight: bold; color: #1e40af; display: block; margin-bottom: 4px; }

    .cash-input { width: 100%; padding: 8px; border: 1px solid #93c5fd; border-radius: 4px; font-size: 16px; font-weight: bold; color: #1e293b; box-sizing: border-box; }

    .change-display { font-size: 15px; font-weight: bold; color: #166534; margin-top: 8px; display: flex; justify-content: space-between; }



    .payment-method { display: flex; gap: 10px; margin-bottom: 12px; }

    .pay-btn { flex: 1; padding: 8px; border: 1px solid #cbd5e1; background: #f8fafc; border-radius: 4px; font-weight: bold; font-size: 12px; cursor: pointer; text-align: center; }

    .pay-btn.active { background: #2563eb; color: white; border-color: #2563eb; }



    .btn-action { width: 100%; padding: 12px; border: none; border-radius: 6px; cursor: pointer; font-weight: bold; font-size: 15px; margin-top: 8px; }

    .btn-checkout { background: #16a34a; color: white; }

    .btn-filter { padding: 8px 14px; background: #2563eb; color: white; border: none; border-radius: 4px; font-weight: bold; cursor: pointer; }



    .nav-bar { display: flex; gap: 10px; margin-bottom: 15px; }

    .nav-btn { padding: 8px 16px; background: #e2e8f0; color: #334155; border-radius: 6px; text-decoration: none; font-weight: 600; font-size: 13px; }

    .nav-btn.active { background: #2563eb; color: white; }



    .status-sync { background: #dcfce7; color: #166534; padding: 6px 12px; border-radius: 20px; font-size: 12px; font-weight: bold; display: inline-block; margin-bottom: 10px; }

    .status-err { background: #fef2f2; color: #991b1b; padding: 6px 12px; border-radius: 20px; font-size: 12px; font-weight: bold; display: inline-block; margin-bottom: 10px; }

    #debugBox { display: none; background: #1e293b; color: #f8fafc; padding: 12px; border-radius: 6px; margin-bottom: 15px; font-family: monospace; font-size: 12px; white-space: pre-wrap; word-break: break-all; }

</style>
<div class="header-row">

    <div>

        <h2>POS Invoice Settlement & Cash Audit</h2>

        <div class="subtitle">{{ company_name }}</div>

    </div>

    <div>

        <a href="http://127.0.0.1:5000" target="\_blank" style="font-size: 13px; text-decoration: none; font-weight: bold; color: #0284c7;">Open in Browser</a>

    </div>

</div>



<div class="nav-bar">

    <a href="/" class="nav-btn active">POS Settlement</a>

    <a href="/history" class="nav-btn">Cash Audit Ledger</a>

    <a href="/export-excel" target="\_blank" class="nav-btn" style="background:#16a34a; color:white;">Export Excel</a>

    <a href="/export-pdf" target="\_blank" class="nav-btn" style="background:#dc2626; color:white;">Export PDF</a>

</div>



<div id="debugBox"></div>



<div class="pos-grid">

    <div class="panel">

        {% if invoices|length > 0 %}

            <div class="status-sync">● Manager.io Cloud Connected ({{ invoices|length }} Invoices Found)</div>

        {% else %}

            <div class="status-err">⚠️ Status: {{ api_status }}</div>

        {% endif %}



        <form method="GET" action="/" class="filter-panel">

            <div class="filter-group">

                <label>Search Invoice / Customer</label>

                <input type="text" name="query" class="filter-input" placeholder="Invoice # or Customer..." value="{{ query }}">

            </div>

            <div class="filter-group">

                <label>Start Date</label>

                <input type="date" name="start_dt" class="filter-input" value="{{ start_dt }}">

            </div>

            <div class="filter-group">

                <label>End Date</label>

                <input type="date" name="end_dt" class="filter-input" value="{{ end_dt }}">

            </div>

            <button type="submit" class="btn-filter">Filter</button>

        </form>



        <div style="max-height: 480px; overflow-y: auto;">

            <table class="invoice-table">

                <thead>

                    <tr>

                        <th>Date</th>

                        <th>Invoice #</th>

                        <th>Customer</th>

                        <th>Amount (LKR)</th>

                    </tr>

                </thead>

                <tbody>

                    {% for inv in invoices %}

                    <tr class="invoice-row" onclick="selectInvoice('{{ inv.key }}', '{{ inv.invoice_number }}', '{{ inv.customer|escape }}', {{ inv.amount }})">

                        <td>{{ inv.date }}</td>

                        <td><strong>{{ inv.invoice_number }}</strong></td>

                        <td>{{ inv.customer }}</td>

                        <td>Rs. {{ "{:,.2f}".format(inv.amount) }}</td>

                    </tr>

                    {% endfor %}

                    {% if invoices|length == 0 %}

                    <tr><td colspan="4" style="text-align: center; color: #64748b; padding: 20px;">No invoices found matching criteria.</td></tr>

                    {% endif %}

                </tbody>

            </table>

        </div>

    </div>



    <div class="panel">

        <h3 style="margin-top: 0; color: #1e293b;">Settlement Details</h3>

        

        <div class="totals-box">

            <div style="margin-bottom: 6px; font-size: 13px; color: #475569;">

                <span>Selected Invoice: </span><strong id="selectedInvNum">None</strong>

            </div>

            <div style="margin-bottom: 6px; font-size: 13px; color: #475569;">

                <span>Customer: </span><strong id="selectedCustomer">N/A</strong>

            </div>

            <div class="total-row grand">

                <span>Amount Due:</span>

                <span id="grandTotal">Rs. 0.00</span>

            </div>

        </div>



        <div class="cash-box">

            <label>PAYMENT METHOD</label>

            <div class="payment-method">

                <div class="pay-btn active" id="pay-CASH" onclick="setPaymentMethod('CASH')">CASH</div>

                <div class="pay-btn" id="pay-CARD" onclick="setPaymentMethod('CARD')">CARD</div>

                <div class="pay-btn" id="pay-ONLINE" onclick="setPaymentMethod('ONLINE')">ONLINE</div>

            </div>



            <div id="cashRenderedGroup">

                <label>CASH RENDERED (LKR)</label>

                <input type="number" id="cashRendered" class="cash-input" placeholder="0.00" onkeyup="calculateChange()" onchange="calculateChange()">

                <div class="change-display">

                    <span>Change Returned:</span>

                    <span id="changeReturned">Rs. 0.00</span>

                </div>

            </div>

        </div>



        <button class="btn-action btn-checkout" id="btnCheckout" onclick="processSettlement()">Complete Settlement & Link Reference</button>

    </div>

</div>

β€œβ€"

RECEIPT_TEMPLATE = β€œβ€"

<title>POS Receipt - {{ pos_ref }}</title>

<style>

    body { font-family: 'Courier New', monospace; margin: 0; padding: 20px; background: #fff; }

    .receipt-box { width: 80mm; margin: 0 auto; padding: 10px; border: 1px dashed #ccc; }

    .header { text-align: center; margin-bottom: 10px; }

    .title { font-weight: bold; font-size: 15px; }

    .sub { font-size: 11px; color: #555; }

    .line { border-bottom: 1px dashed #000; margin: 8px 0; }

    table { width: 100%; font-size: 12px; }

    .num { text-align: right; }

    .words { font-size: 10px; margin-top: 8px; font-style: italic; }

    .no-print { text-align: center; margin-bottom: 15px; }

    .btn-print { padding: 8px 16px; background: #2563eb; color: white; border: none; border-radius: 4px; cursor: pointer; }

    @media print { .no-print { display: none; } .receipt-box { border: none; } }

</style>
<button onclick="window.print()" class="btn-print">πŸ–¨οΈ Print Receipt</button>
<div class="header">

    <div class="title">{{ company_name }}</div>

    <div class="sub">Official Payment Receipt</div>

    <div class="sub">POS Ref: {{ pos_ref }}</div>

    <div class="sub">Manager Invoice Ref: {{ invoice_number }}</div>

    <div class="sub">Date/Time: {{ date }}</div>

</div>

<div class="line"></div>

<table>

    <tr><td>Customer:</td><td class="num">{{ customer }}</td></tr>

    <tr><td><strong>TOTAL SETTLED:</strong></td><td class="num"><strong>Rs. {{ "{:,.2f}".format(total) }}</strong></td></tr>

    <tr><td>Payment Method:</td><td class="num">{{ payment_method }}</td></tr>

    {% if payment_method == 'CASH' %}

    <tr><td>Cash Rendered:</td><td class="num">Rs. {{ "{:,.2f}".format(cash_rendered) }}</td></tr>

    <tr><td>Change Returned:</td><td class="num">Rs. {{ "{:,.2f}".format(change_returned) }}</td></tr>

    {% endif %}

</table>

<div class="line"></div>

<div class="words">{{ words }}</div>

<div class="line"></div>

<div class="header" style="font-size: 10px; margin-top: 10px;">Payment Linked to Manager.io Cloud</div>

β€œβ€"

LEDGER_TEMPLATE = β€œβ€"

<title>Cash Audit Ledger - {{ company_name }}</title>

<style>

    body { font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; background: #f4f6f9; margin: 0; padding: 20px; }

    .container { max-width: 1300px; margin: 0 auto; background: white; padding: 25px; border-radius: 8px; box-shadow: 0 4px 12px rgba(0,0,0,0.1); }

    h2 { color: #1e293b; margin-top: 0; }

    

    .nav-bar { display: flex; gap: 10px; margin-bottom: 20px; }

    .nav-btn { padding: 8px 16px; background: #e2e8f0; color: #334155; border-radius: 6px; text-decoration: none; font-weight: 600; font-size: 13px; }

    .nav-btn.active { background: #2563eb; color: white; }



    table { width: 100%; border-collapse: collapse; }

    th, td { padding: 10px 12px; border-bottom: 1px solid #e2e8f0; text-align: left; font-size: 13px; }

    th { background: #f1f5f9; color: #475569; }

    .audit-pill { background: #f1f5f9; border: 1px solid #cbd5e1; padding: 3px 6px; border-radius: 4px; font-size: 11px; font-weight: bold; }

</style>
<h2>POS Cash Rendered Audit Ledger</h2>



<div class="nav-bar">

    <a href="/" class="nav-btn">POS Settlement</a>

    <a href="/history" class="nav-btn active">Cash Audit Ledger</a>

    <a href="/export-excel" target="\_blank" class="nav-btn" style="background:#16a34a; color:white;">Export Excel</a>

    <a href="/export-pdf" target="\_blank" class="nav-btn" style="background:#dc2626; color:white;">Export PDF</a>

</div>



<table>

    <thead>

        <tr>

            <th>Date / Time</th>

            <th>POS Ref</th>

            <th>Manager Invoice #</th>

            <th>Customer</th>

            <th>Method</th>

            <th>Settled Amt</th>

            <th>Cash Rendered</th>

            <th>Change Returned</th>

            <th>Receipt</th>

        </tr>

    </thead>

    <tbody>

        {% for t in transactions %}

        <tr>

            <td>{{ t.date }}</td>

            <td><strong>{{ t.pos_ref }}</strong></td>

            <td>{{ t.invoice_number }}</td>

            <td>{{ t.customer }}</td>

            <td><span class="audit-pill">{{ t.payment_method }}</span></td>

            <td>Rs. {{ "{:,.2f}".format(t.total) }}</td>

            <td>Rs. {{ "{:,.2f}".format(t.cash_rendered) }}</td>

            <td><span style="color: #166534; font-weight: bold;">Rs. {{ "{:,.2f}".format(t.change_returned) }}</span></td>

            <td><a href="/receipt?ref={{ t.pos_ref }}" target="\_blank" style="color:#2563eb; font-weight:bold;">Receipt</a></td>

        </tr>

        {% endfor %}

        {% if transactions|length == 0 %}

        <tr><td colspan="9" style="text-align: center; color: #64748b; padding: 25px;">No settlement audit records logged yet.</td></tr>

        {% endif %}

    </tbody>

</table>

β€œβ€"

# ==========================================

# FLASK ROUTES

# ==========================================

@app.route(β€˜/’)

def index():

query = request.args.get('query', '')

start_dt = request.args.get('start_dt', '')

end_dt = request.args.get('end_dt', '')



invoices, status = fetch_invoices_from_manager(query, start_dt, end_dt)

return render_template_string(

    POS_TEMPLATE, 

    invoices=invoices, 

    api_status=status, 

    company_name=COMPANY_NAME,

    query=query,

    start_dt=start_dt,

    end_dt=end_dt

)

@app.route(β€˜/history’)

def history():

return render_template_string(LEDGER_TEMPLATE, transactions=TRANSACTIONS, company_name=COMPANY_NAME)

@app.route(β€˜/settle-invoice’, methods=[β€˜POST’])

def settle_invoice():

try:

    data = request.get_json(force=True) or {}

    inv_key = data.get('invoice_key')

    inv_num = data.get('invoice_number')

    customer = data.get('customer')

    amount = float(data.get('amount', 0.0))

    payment_method = data.get('payment_method', 'CASH')

    cash_rendered = float(data.get('cash_rendered', 0.0))

    change_returned = float(data.get('change_returned', 0.0))



    if not inv_key:

        return jsonify({"success": False, "error": "No invoice selected."})



    pos_ref = f"POS-{inv_num}-{int(datetime.now().timestamp())}"



    success, ref_or_err = link_receipt_to_manager_invoice(inv_key, pos_ref, amount, payment_method)

    if not success:

        return jsonify({"success": False, "error": ref_or_err})



    date_str = datetime.now().strftime("%Y-%m-%d %H:%M:%S")



    tx = {

        "pos_ref": pos_ref,

        "invoice_number": inv_num,

        "customer": customer,

        "date": date_str,

        "total": amount,

        "payment_method": payment_method,

        "cash_rendered": cash_rendered if payment_method == 'CASH' else amount,

        "change_returned": change_returned if payment_method == 'CASH' else 0.0

    }

    TRANSACTIONS.insert(0, tx)



    return jsonify({"success": True, "pos_ref": pos_ref})

except Exception as e:

    return jsonify({"success": False, "error": str(e)})

@app.route(β€˜/receipt’)

def print_receipt():

ref = request.args.get('ref', '')

tx = next((t for t in TRANSACTIONS if t\['pos_ref'\] == ref), None)

if not tx:

    return "Receipt not found.", 404



words = amount_to_words(tx\['total'\])

return render_template_string(

    RECEIPT_TEMPLATE,

    company_name=COMPANY_NAME,

    pos_ref=tx\['pos_ref'\],

    invoice_number=tx\['invoice_number'\],

    customer=tx\['customer'\],

    date=tx\['date'\],

    total=tx\['total'\],

    payment_method=tx\['payment_method'\],

    cash_rendered=tx\['cash_rendered'\],

    change_returned=tx\['change_returned'\],

    words=words

)

@app.route(β€˜/export-excel’)

def export_excel():

output = io.StringIO()

writer = csv.writer(output)

writer.writerow(\['Date/Time', 'POS Ref', 'Manager Invoice #', 'Customer', 'Payment Method', 'Settled (LKR)', 'Cash Rendered (LKR)', 'Change Returned (LKR)'\])



for t in TRANSACTIONS:

    writer.writerow(\[

        t\['date'\], 

        t\['pos_ref'\], 

        t\['invoice_number'\],

        t\['customer'\],

        t\['payment_method'\], 

        f"{t\['total'\]:.2f}", 

        f"{t\['cash_rendered'\]:.2f}", 

        f"{t\['change_returned'\]:.2f}"

    \])



output.seek(0)

filename = f"POS_Cash_Audit\_{datetime.now().strftime('%Y%m%d\_%H%M%S')}.csv"



return Response(

    output.getvalue(),

    mimetype="text/csv",

    headers={"Content-Disposition": f"attachment;filename={filename}"}

)

@app.route(β€˜/export-pdf’)

def export_pdf():

buffer = io.BytesIO()

doc = SimpleDocTemplate(buffer, pagesize=letter)

styles = getSampleStyleSheet()

elements = \[\]



title_style = ParagraphStyle('DocTitle', parent=styles\['Heading1'\], fontSize=16, leading=20, textColor=colors.HexColor('#1e293b'))

subtitle_style = ParagraphStyle('DocSubtitle', parent=styles\['Normal'\], fontSize=11, leading=14, textColor=colors.HexColor('#64748b'))



elements.append(Paragraph(f"{COMPANY_NAME} - Cash Audit Report", title_style))

elements.append(Paragraph(f"Generated: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}", subtitle_style))

elements.append(Spacer(1, 15))



table_data = \[\['Date/Time', 'POS Ref', 'Invoice #', 'Method', 'Settled', 'Rendered', 'Change'\]\]

for t in TRANSACTIONS:

    table_data.append(\[

        t\['date'\],

        t\['pos_ref'\],

        t\['invoice_number'\],

        t\['payment_method'\],

        f"Rs. {t\['total'\]:,.2f}",

        f"Rs. {t\['cash_rendered'\]:,.2f}",

        f"Rs. {t\['change_returned'\]:,.2f}"

    \])



t = Table(table_data, colWidths=\[100, 110, 70, 60, 70, 70, 70\])

t.setStyle(TableStyle(\[

    ('BACKGROUND', (0, 0), (-1, 0), colors.HexColor('#2563eb')),

    ('TEXTCOLOR', (0, 0), (-1, 0), colors.white),

    ('FONTNAME', (0, 0), (-1, 0), 9),

    ('GRID', (0, 0), (-1, -1), 0.5, colors.HexColor('#cbd5e1')),

    ('VALIGN', (0, 0), (-1, -1), 'MIDDLE'),

    ('FONTNAME', (0, 1), (-1, -1), 'Helvetica'),

    ('FONTSIZE', (0, 1), (-1, -1), 8),

    ('ROWBACKGROUNDS', (0, 1), (-1, -1), \[colors.white, colors.HexColor('#f8fafc')\])

\]))



elements.append(t)

doc.build(elements)



buffer.seek(0)

filename = f"POS_Cash_Audit\_{datetime.now().strftime('%Y%m%d\_%H%M%S')}.pdf"



return send_file(

    buffer,

    as_attachment=True,

    download_name=filename,

    mimetype='application/pdf'

)

def run_flask():

app.run(host="127.0.0.1", port=5000, debug=False, use_reloader=False)

if _name_ == β€˜_main_’:

t = threading.Thread(target=run_flask)

t.daemon = True

t.start()



window = webview.create_window(

    title=f"POS Invoice Settlement - {COMPANY_NAME}",

    url="http://127.0.0.1:5000",

    width=1350,

    height=850,

    resizable=True

)



webview.start()

sys.exit()

Above is a simple POS I tried to create. However, 401 error comes. Can you kindly help on this script to avoid this error?

I’m not entirely sure what the specific issue is in your case, but from looking at your code it seems you’re trying to create both a sales invoice and a receipt using API2.

You might find the following example useful as a reference:

#!/usr/bin/env python3
"""
Create a Sales Invoice and a Receipt in Manager.io via /api2, using an
access token (X-API-KEY) β€” the same credential type generated under
Settings > Access Tokens in Manager.

Fill in the configuration values below, then run:
    pip install requests
    python manager_api2_example.py
"""

import sys

import requests

# ── Configuration β€” fill these in ────────────────────────────────────────
SUBDOMAIN = ""          # from https://<SUBDOMAIN>.manager.io
API_KEY = ""            # the "Secret" value shown once when creating an Access Token

CUSTOMER_KEY = ""       # GUID β€” see find_customer_key_by_name() below for how to look one up
ITEM_KEY = ""           # GUID of a sale item (Inventory Item or Non-inventory Item)
BANK_ACCOUNT_KEY = ""   # GUID of a Bank/Cash account
# ──────────────────────────────────────────────────────────────────────────

BASE_URL = f"https://{SUBDOMAIN}.manager.io"


def api2_get(session: requests.Session, path: str, params: dict | None = None) -> dict:
    response = session.get(f"{BASE_URL}/api2{path}", params=params)
    response.raise_for_status()
    return response.json()


def api2_post(session: requests.Session, path: str, body: dict) -> dict:
    response = session.post(f"{BASE_URL}/api2{path}", json=body)
    response.raise_for_status()
    return response.json()


def find_customer_key_by_name(session: requests.Session, name: str) -> str:
    """Example of resolving a GUID from a human-readable name via a list endpoint."""
    data = api2_get(session, "/customers")
    for row in data.get("customers", []):
        if row.get("name") == name:
            return row["key"]
    raise ValueError(f"No customer found named {name!r}")


def create_sales_invoice(
    session: requests.Session,
    customer_key: str,
    item_key: str,
    issue_date: str,   # "YYYY-MM-DD"
    reference: str,
    qty: float,
    unit_price: float,
    description: str = "",
) -> dict:
    body = {
        "IssueDate": issue_date,
        "Reference": reference,
        "Customer": customer_key,
        "Lines": [
            {
                "Item": item_key,
                "Qty": qty,
                "SalesUnitPrice": unit_price,
                "LineDescription": description,
            }
        ],
    }
    return api2_post(session, "/sales-invoice-form", body)


def create_receipt(
    session: requests.Session,
    customer_key: str,
    item_key: str,
    bank_account_key: str,
    date: str,   # "YYYY-MM-DD"
    reference: str,
    amount: float,
    description: str = "",
) -> dict:
    body = {
        "Date": date,
        "Reference": reference,
        "PaidBy": 1,   # PayerPayeeType: 1 = Customer, 2 = Supplier, 0 = Other
        "Customer": customer_key,
        "ReceivedIn": bank_account_key,
        "Lines": [
            {
                "Item": item_key,
                "Amount": amount,
                "LineDescription": description,
            }
        ],
    }
    return api2_post(session, "/receipt-form", body)


def main() -> None:
    if not API_KEY:
        sys.exit("Fill in API_KEY at the top of this file before running.")
    if not (CUSTOMER_KEY and ITEM_KEY and BANK_ACCOUNT_KEY):
        sys.exit(
            "Fill in CUSTOMER_KEY, ITEM_KEY and BANK_ACCOUNT_KEY β€” "
            "see find_customer_key_by_name() for how to look up a GUID by name."
        )

    session = requests.Session()
    session.headers["X-API-KEY"] = API_KEY

    print("Creating sales invoice...")
    invoice = create_sales_invoice(
        session, CUSTOMER_KEY, ITEM_KEY,
        issue_date="2026-01-15", reference="INV-0001",
        qty=2, unit_price=150.00, description="Consulting hours",
    )
    print(f"  Created: {invoice['Key']}")

    print("Creating receipt...")
    receipt = create_receipt(
        session, CUSTOMER_KEY, ITEM_KEY, BANK_ACCOUNT_KEY,
        date="2026-01-20", reference="RCPT-0001",
        amount=300.00, description="Payment received",
    )
    print(f"  Created: {receipt['Key']}")


if __name__ == "__main__":
    try:
        main()
    except requests.HTTPError as exc:
        status = exc.response.status_code if exc.response is not None else "?"
        body = exc.response.text if exc.response is not None else str(exc)
        print(f"Request failed: {status} {body}", file=sys.stderr)
        sys.exit(1)