Luna SaludAPI ReferenceLuna Salud

Create a payment

POST/payments

Records a payment your clinic received in cash, by bank transfer, from an insurer or by another method and creates the charge it pays: catalog services and free-text concepts, each line at its catalog price unless you send unitPrice. An amount equal to the total marks the payment PAID; a smaller amount records it as an anticipo and leaves it PENDING with the balance owed. Card payments are not recorded through the API. Nothing is sent to the patient: no receipt, no reminder. idempotencyKey is required: retrying with the same key and body returns the payment already recorded instead of a duplicate, and a request still in progress with that key answers 409 until it finishes. Requires an API key with the write scope.

Code samples

Node.js

const fetch = require('node-fetch');

async function callApi() {
  const apiKey = 'YOUR_API_KEY';
  const url = `https://account.lunahealth.app/api/payments`;
  const requestBody = {
    // Add your request body data here
  };

  try {
    const response = await fetch(url, {
      method: 'POST',
      headers: {
        'Authorization': `Bearer ${apiKey}`,
        'Content-Type': 'application/json'
      },
      body: JSON.stringify(requestBody)
    });
    
    if (!response.ok) {
      throw new Error(`API error: ${response.status}`);
    }
    
    const data = await response.json();
    console.log(data);
    return data;
  } catch (error) {
    console.error('Error calling API:', error);
    throw error;
  }
}

callApi();

Python

import requests

def call_api():
    api_key = 'YOUR_API_KEY'
    
    url = 'https://account.lunahealth.app/api/payments'
    
    headers = {
        'Authorization': f'Bearer {api_key}',
        'Content-Type': 'application/json'
    }
    
    params = {

    }
    
    # Add your request body data here
    request_body = {}
        try:
        response = requests.post(
            url,
            headers=headers,
            params=params,
            json=request_body
        )
        response.raise_for_status()
        
        data = response.json()
        print(data)
        return data
    except requests.exceptions.RequestException as e:
        print(f'Error calling API: {e}')
        raise

call_api()

Request body

Required

Request fields

NameTypeRequiredDescription
idempotencyKeystringYesA unique key you generate for this payment, e.g. a UUID. Retrying with the same key and the same body returns the payment the first request created instead of recording it again; the same key with a different body is refused.
patientIdstringYesID of the patient who paid, in your organization
appointmentIdstringNoID of the patient's appointment this payment is for. An appointment can carry one payment. When sent, the payment is attributed to the appointment's providers unless you send staffId, and the branch defaults to the appointment's.
staffIdstringNoID of the provider the payment is attributed to. Required unless appointmentId is sent.
locationIdstringNoID of the branch the payment belongs to. Defaults to the appointment's branch, if any.
issueDatestringNoIssue date as YYYY-MM-DD, on the clinic's calendar. Defaults to today in the clinic's timezone.
linesarray<object>YesWhat the payment is for: catalog services and free-text concepts, 1 to 50 lines.
paymentobjectYesThe money received.

Example request

{
  "idempotencyKey": "string",
  "patientId": "string",
  "appointmentId": "string",
  "staffId": "string",
  "locationId": "string",
  "issueDate": "string",
  "lines": [
    {
      "serviceId": "string",
      "description": "string",
      "quantity": 0,
      "unitPrice": 0
    }
  ],
  "payment": {
    "amount": 0,
    "method": "CASH",
    "methodDetail": "string",
    "insurerId": "string"
  }
}

Responses

StatusDescription
200Successful response
400Invalid input data
401Authorization not provided
403Insufficient access
500Internal server error

200 — Successful response

Response fields

NameTypeRequiredDescription
idstringYes—
invoice_numberstringYes—
statusstringYes—
currencystringnullYes—
total_amountnumberYes—
subtotal_amountnumberYes—
tax_amountnumberYes—
paid_amountnumbernullYes—
balancenumberYes—
payment_methodstring | nullYes—
payment_datestringnullYes—
due_datestringYes—
created_atstringYes—
updated_atstringYes—
patientobject | nullYes—
cfdiobject | nullYes—

Example response

{
  "id": "string",
  "invoice_number": "string",
  "status": "PAID",
  "currency": null,
  "total_amount": 0,
  "subtotal_amount": 0,
  "tax_amount": 0,
  "paid_amount": null,
  "balance": 0,
  "payment_method": "CARD",
  "payment_date": null,
  "due_date": "string",
  "created_at": "string",
  "updated_at": "string",
  "patient": {
    "id": "string",
    "first_name": null,
    "last_name": null,
    "email": null
  },
  "cfdi": {
    "uuid": null,
    "serie": null,
    "folio": "string",
    "status": "DRAFT",
    "stamped_at": null,
    "cancelled_at": null
  }
}

400 — Invalid input data

Response fields

NameTypeRequiredDescription
messagestringYesThe error message
codestringYesThe error code
issuesarray<object>NoAn array of issues that were responsible for the error

Example response

{
  "code": "BAD_REQUEST",
  "message": "Invalid input data",
  "issues": []
}

401 — Authorization not provided

Response fields

NameTypeRequiredDescription
messagestringYesThe error message
codestringYesThe error code
issuesarray<object>NoAn array of issues that were responsible for the error

Example response

{
  "code": "UNAUTHORIZED",
  "message": "Authorization not provided",
  "issues": []
}

403 — Insufficient access

Response fields

NameTypeRequiredDescription
messagestringYesThe error message
codestringYesThe error code
issuesarray<object>NoAn array of issues that were responsible for the error

Example response

{
  "code": "FORBIDDEN",
  "message": "Insufficient access",
  "issues": []
}

500 — Internal server error

Response fields

NameTypeRequiredDescription
messagestringYesThe error message
codestringYesThe error code
issuesarray<object>NoAn array of issues that were responsible for the error

Example response

{
  "code": "INTERNAL_SERVER_ERROR",
  "message": "Internal server error",
  "issues": []
}