docs
/
Courses — AppEngine

AppEngine CRM APIs: Leads, Reservations, Forms and Support Tickets

Watch on YouTube ↗

Video chapters
  1. AppEngine CRM APIs: leads, bookings, forms and support
  2. Built-in records and business actions
  3. Prepare the API address and caller identity
  4. Create a pipeline with real stage identifiers
  5. Capture and retrieve a lead
  6. Assign, follow up, qualify and convert
  7. Configure a bookable service
  8. Discover available times in the business timezone
  9. Book the slot and handle a conflict
  10. Reschedule, cancel and inspect the retained record
  11. Define the intake schema and access mode
  12. Validate and read back a form submission
  13. Create a ticket as a signed-in customer
  14. Add a staff note, assign and resolve
  15. Read customer context and pipeline measurements
  16. Choose the next CRM capability deliberately
  17. Find the same CRM records in Appmint Studio
  18. Connect each CRM action to its actual result

Written guide

Companion source code

Get the course source code on GitHub. The folder includes the supplied examples and setup instructions. Keep credentials in your own environment; use the maintained API reference when adapting recorded request examples.

documented and rehearsed developer course · 18 chapters · Current API and Studio evidence

Watch the review video · Download runnable examples and observed JSON

How to use this course

Choose the operation you need. JSON bodies are shown completely in the downloadable examples; the video displays equivalent TypeScript, JavaScript, Python, curl and Postman requests alongside JSON. Observed responses are evidence from the controlled training deployment, not templates to post back. Historical IDs and dates must be replaced. The examples README includes exact connection setup, sequence and expected failures.

1. AppEngine CRM APIs: leads, bookings, forms and support

Your application can capture a lead, connect that person to a customer, offer bookable times, collect a form, and open a support ticket. AppEngine supplies the records and the actions behind those screens. We'll follow real API requests through their saved results, then inspect the same information in Appmint Studio. You'll see the required setup, the different calling identities, and how to handle a request that cannot be completed.

This course is for developers adding AppEngine CRM features to their own applications. It teaches the platform contracts and setup rather than how to sell or operate a fictional company. Four independent, short examples cover a lead, a booking, an intake form and a support ticket. You can complete one without implementing the others. The advanced section identifies additional CRM capabilities and gives exact starting contracts and linked lessons; those alternatives were documented, not executed as external transactions.

AppEngine CRM APIs: leads, bookings, forms and support — concept illustration

AppEngine CRM APIs: leads, bookings, forms and support — concept illustration

2. Built-in records and business actions

A lead, customer, reservation, and ticket are built-in record types. A business action does more than change a field. Converting a lead links its customer. Booking checks the requested time and derives the end time from its service. Resolving a ticket records the resolution and updates its workflow. Use the action for that transition, and read the resulting record to confirm what happened. Repository remains useful for schema extensions, configuration and data inspection; it is not a replacement for the booking or conversion operation.

A built-in record has a datatype, an identifier (sk) and business values under data. A CRM action can change several related values or records. Lead conversion can connect a customer and change a pipeline stage; booking checks a slot and records a reservation; resolving a ticket updates its resolution and workflow. A Repository write used to configure a definition is not evidence that the business action has run. Use the action endpoint, preserve its returned identifiers, then inspect its specific result. Repository readback is useful for authorized staff inspection.

Built-in records and business actions — concept illustration

Built-in records and business actions — concept illustration

3. Prepare the API address and caller identity

Start with your organization's API address, organization ID, and an authorized staff session. Database and Repository APIs, chapter two, shows the staff sign-in and returns the token used here. Keep orgid and the bearer token on each protected request. Public booking and the open form in this example use the organization header without a staff token. Customer ticket intake uses a customer session from API Foundations. Do not put a staff credential into a public website. Training email delivery is disabled in this organization; queued notices will not be presented as delivered messages.

  1. Open Appmint Studio in a training organization. Keep its organization ID and assigned AppEngine API origin. The Studio website address is not a replacement for your backend API origin.
  2. Follow Database & Repository APIs — staff setup, video chapter 2 at 1:02 and its written setup. Obtain a valid staff token from POST /profile/user/signin with orgid and your staff email/password. Handle any challenge returned by your deployment. Keep that token on a trusted server or development tool.
  3. For the ticket chapter only, use the customer sign-in demonstrated in API Foundations. POST /profile/customer/signin produces the customer session used in that chapter. A staff token and a customer token are not interchangeable.
  4. Use controlled training email addresses. Inspect your organization's delivery configuration before writes. This rehearsal verified inactive SMTP with sendOutgoing: false; it did not prove delivery was disabled for every possible channel. Do not assume your own organization has the same configuration.
  5. Download the examples. Set APPENGINE_ENDPOINT, ORG_ID, STAFF_TOKEN, and, for customer-only requests, CUSTOMER_TOKEN. Choose one client implementation; do not create the same records once per language. Node 20+ and Python 3.10+ runners use standard libraries. The TypeScript equivalent targets an existing Node TypeScript project. Postman variables stay in your local environment.
  6. Replace every recorded identifier with your returned ID, and replace historical booking and follow-up dates. future-slots.py YOUR_DEFINITION_ID generates a future weekday request in America/Chicago without making a booking. Read the returned available slot and use that exact timestamp; do not convert a displayed wall-clock time by guessing the UTC offset.

Public definitions, slot lookup and booking in this lesson use orgid without a staff token. No application credential is a prerequisite for those demonstrated routes. The runner's route.json explicitly identifies staff, customer or public. The public form in this lesson intentionally uses authenticationType: none.

Prepare the API address and caller identity — request or observed-response teaching frame

Prepare the API address and caller identity — request or observed-response teaching frame

4. Create a pipeline with real stage identifiers

First create the pipeline the lead will belong to. The request contains its name and stages, each with a stable ID, label, order and probability. Our final stages identify won and lost outcomes. This is a separate training pipeline, not a change to your organization's default. Save the returned pipeline ID. When creating a lead, send that ID and the intended starting stage ID. Read the pipeline back to confirm the saved stages. Assignment rules, stage requirements and scoring choices belong to the pipeline you actually use; a label typed on your own board does not configure them.

Create a non-default training pipeline first. The complete request below includes ordered stage IDs, probability, Won/Lost flags and an empty assignment-rules list. Save the returned sk as your pipeline ID. The stage ID is new, not the label New. Read the saved pipeline and compare all stages before creating a lead. Do not overwrite a business's existing default pipeline to follow this example.

Create a pipeline with real stage identifiers — request or observed-response teaching frame

Create a pipeline with real stage identifiers — request or observed-response teaching frame

Create pipeline

POST /crm/leads/pipelines · Identity: staff · Observed HTTP 201.

{
  "name": "CRM API course",
  "description": "Controlled developer course pipeline",
  "isDefault": false,
  "stages": [
    {
      "id": "new",
      "name": "New",
      "order": 0,
      "probability": 10
    },
    {
      "id": "qualified",
      "name": "Qualified",
      "order": 1,
      "probability": 50
    },
    {
      "id": "won",
      "name": "Won",
      "order": 2,
      "probability": 100,
      "isWon": true
    },
    {
      "id": "lost",
      "name": "Lost",
      "order": 3,
      "probability": 0,
      "isLost": true
    }
  ],
  "assignmentRules": []
}

Complete request metadata and observed response. Replace recorded IDs with your own returned values before running.

Pipeline read

GET /crm/leads/pipelines/6aca82e237ff863efff476aa · Identity: staff · Observed HTTP 200.

Complete request metadata and observed response. Replace recorded IDs with your own returned values before running.

5. Capture and retrieve a lead

Lead capture uses CRM leads detail, with fields directly in the request body. Here we send the person's name, email, website source, description, pipeline ID and starting stage. The result has a generated lead number, separate name fields, a score, and a contact ID. Keep the record's sk for subsequent actions; the display number is a different value. The list endpoint supports search and pagination. Searching for this lead returns the same saved record. The score reflects the supplied information and current rules; do not treat the example value as a universal qualification threshold.

Replace pipelineId with your new pipeline's ID; retain stageId: new. Use a distinct controlled email and submit once. Save the lead's sk separately from its contactId; they refer to different records. Search results use paging, so inspect total and data rather than treating one page as the whole organization. The recorded search was taken after conversion; its later status is not proof that creation alone converts a lead.

Capture and retrieve a lead — request or observed-response teaching frame

Capture and retrieve a lead — request or observed-response teaching frame

Create lead

POST /crm/leads/detail · Identity: staff · Observed HTTP 201.

{
  "fullName": "Mira CRM Course",
  "email": "[email protected]",
  "source": "website",
  "description": "Controlled API inquiry",
  "pipelineId": "6aca82e237ff863efff476aa",
  "stageId": "new"
}

Complete request metadata and observed response. Replace recorded IDs with your own returned values before running.

Lead filter

GET /crm/leads/detail?search=Mira%20CRM&page=1&pageSize=10 · Identity: staff · Observed HTTP 200.

Complete request metadata and observed response. Replace recorded IDs with your own returned values before running.

6. Assign, follow up, qualify and convert

Assign the lead with assignedTo. This example assigns it to the signed-in staff member. Add a note through activities, and set a follow-up with an actual future timestamp. Qualification records the decision and its notes. Conversion then connects the lead to a customer. Our returned customer ID matches the existing contact ID, so this conversion did not create a second person. This configured pipeline also moves the converted lead to its Won stage. Finally read activities: creation, assignment, notes and transitions are visible there. Conversion is not a recorded payment or an invoice.

Run the operations in order against your returned lead ID. Assign to your authorized training staff email; this rehearsal assigned the caller to themselves. Add an activity with type and description, then set a future followUpDate. Qualification and conversion are separate actions. Save the converted customer ID and compare it with contactId; the observed values match. Our configured Won stage was selected by conversion. This is a pipeline outcome, not payment or revenue collection. Activity history provides creation, assignment, note and transition entries; read it after mutation. A forecast aggregates configured lead values and probabilities; it is not a ledger of cash received.

Assign, follow up, qualify and convert — request or observed-response teaching frame

Assign, follow up, qualify and convert — request or observed-response teaching frame

Assign

POST /crm/leads/assign/6aca82e237ff863efff476ad · Identity: staff · Observed HTTP 201.

{
  "assignedTo": "[email protected]"
}

Complete request metadata and observed response. Replace recorded IDs with your own returned values before running.

Note

POST /crm/leads/activities/6aca82e237ff863efff476ad · Identity: staff · Observed HTTP 201.

{
  "type": "note",
  "description": "Requested the API integration guide"
}

Complete request metadata and observed response. Replace recorded IDs with your own returned values before running.

Follow up

POST /crm/leads/follow-up/6aca82e237ff863efff476ad · Identity: staff · Observed HTTP 201.

{
  "followUpDate": "2026-10-14T18:24:34.989434+00:00",
  "notes": "Review the requested guide"
}

Complete request metadata and observed response. Replace recorded IDs with your own returned values before running.

Qualify

POST /crm/leads/qualify/6aca82e237ff863efff476ad · Identity: staff · Observed HTTP 201.

{
  "notes": "Integration requirements confirmed"
}

Complete request metadata and observed response. Replace recorded IDs with your own returned values before running.

Convert

POST /crm/leads/convert/6aca82e237ff863efff476ad · Identity: staff · Observed HTTP 201.

{
  "conversionValue": 150
}

Complete request metadata and observed response. Replace recorded IDs with your own returned values before running.

Lead activities

GET /crm/leads/activities/6aca82e237ff863efff476ad · Identity: staff · Observed HTTP 200.

Complete request metadata and observed response. Replace recorded IDs with your own returned values before running.

Customer

GET /repository/get/customer/6aca82e237ff863efff476ab · Identity: staff · Observed HTTP 200.

Complete request metadata and observed response. Replace recorded IDs with your own returned values before running.

7. Configure a bookable service

Before asking for times, create a reservation definition. Our complete payload defines an active service called API consultation, lasting thirty minutes, one available place, weekdays, opening hours, and America Chicago as the business timezone. It also specifies the reminder configuration. This is configuration data; the actual booking will use the reservation action. The returned definition ID connects every slot request and reservation to these rules. Definitions can also describe hosts, locations and blocked times. Configure those when your service needs them, and verify their effect through availability rather than guessing from a calendar display.

Create the definition before searching for times. The exact payload supplies service name and duration, active status, weekdays, hours, timezone, capacity and reminders. spots: 1 limits this definition's available place count. hosts: [] is the simplified unassigned-host example; it does not teach a configured staff rota. Save the definition sk. The definition name and the service name are different values; slot lookup needs the service name exactly as configured. Reminder configuration may queue a delivery; it is not evidence a recipient received it.

Configure a bookable service — request or observed-response teaching frame

Configure a bookable service — request or observed-response teaching frame

Definition

PUT /repository/create · Identity: staff · Observed HTTP 200.

{
  "datatype": "reservation_definition",
  "isNew": true,
  "data": {
    "name": "crm-api-consultation-1010",
    "title": "CRM API Consultation",
    "type": "service",
    "status": "active",
    "services": [
      {
        "name": "API consultation",
        "duration": 30,
        "price": 0,
        "breakAfter": 0
      }
    ],
    "workDays": [
      "Monday",
      "Tuesday",
      "Wednesday",
      "Thursday",
      "Friday"
    ],
    "officeHours": {
      "startTime": "09:00",
      "endTime": "17:00",
      "timezone": "America/Chicago"
    },
    "spots": 1,
    "hosts": [],
    "notifications": [
      {
        "timeBefore": 1,
        "unit": "days",
        "type": "email"
      }
    ]
  }
}

Complete request metadata and observed response. Replace recorded IDs with your own returned values before running.

Public definitions

GET /crm/reservations/definitions · Identity: public (orgid only) · Observed HTTP 200.

Complete request metadata and observed response. Replace recorded IDs with your own returned values before running.

8. Discover available times in the business timezone

The slots request needs the definition ID, exact service name, and a calendar date. Its response gives start and end instants, places available, and the business timezone. Fourteen hundred UTC is nine in the morning in Chicago on the date shown. Keep the ISO instant for the booking and format the display in the business timezone. For a longer chooser, availability accepts a starting date and number of days and returns the next available time. Use future dates when following this lesson. An available slot is an offer at the time of the query, not a reservation.

Generate a future service date, pass your definition ID, and inspect the returned slots. The response includes actual ISO start/end instants and business-timezone context. For a booking, copy one currently available startTime. The broader availability endpoint reports a date range and next available time. Availability is a read at a moment in time: a later booking request must still be checked for refusal. Change timezone/host/location rules in the definition and re-query availability when using those alternatives.

Discover available times in the business timezone — request or observed-response teaching frame

Discover available times in the business timezone — request or observed-response teaching frame

Slots

POST /crm/reservations/slots · Identity: public (orgid only) · Observed HTTP 201.

{
  "reservationDefinitionId": "6aca82e337ff863efff476b7",
  "serviceName": "API consultation",
  "serviceDate": "2026-10-13"
}

Complete request metadata and observed response. Replace recorded IDs with your own returned values before running.

Availability

POST /crm/reservations/availability · Identity: public (orgid only) · Observed HTTP 201.

{
  "reservationDefinitionId": "6aca82e337ff863efff476b7",
  "serviceName": "API consultation",
  "from": "2026-10-13",
  "days": 2
}

Complete request metadata and observed response. Replace recorded IDs with your own returned values before running.

9. Book the slot and handle a conflict

The public book action accepts the definition, service, selected start time, and the customer's name and contact details. The saved reservation has its own ID and booking code, a calculated end time, and a linked customer. The returned notices say queued. That means delivery work was queued, not that an email arrived. A second request for the occupied time returns HTTP four hundred with SLOT TAKEN in this run. Keep the entered details, show that specific problem, and request fresh availability. Do not bypass this refusal by inserting a reservation through the generic Repository API.

Submit the selected slot, definition/service and controlled customer name/email. Save the booking sk, its human-facing booking code/name, linked customer ID and start/end times. Those are different identifiers. The response's notice entry reported queued state; the training email configuration did not deliver a confirmation. Show a user “booking saved” from the saved record, and only show “confirmation delivered” if you separately verified delivery. A second sequential booking into the same single-place slot returned HTTP 400 with SLOT_TAKEN. Refresh availability and let the user choose another slot. This is an occupied-slot test, not a concurrency stress test.

Book the slot and handle a conflict — request or observed-response teaching frame

Book the slot and handle a conflict — request or observed-response teaching frame

Public book

POST /crm/reservations/book · Identity: public (orgid only) · Observed HTTP 201.

{
  "reservationDefinitionId": "6aca82e337ff863efff476b7",
  "service": "API consultation",
  "startTime": "2026-10-13T14:00:00.000Z",
  "customer": {
    "name": "Mira CRM Course",
    "email": "[email protected]"
  }
}

Complete request metadata and observed response. Replace recorded IDs with your own returned values before running.

Slot conflict

POST /crm/reservations/book · Identity: public (orgid only) · Observed HTTP 400.

{
  "reservationDefinitionId": "6aca82e337ff863efff476b7",
  "service": "API consultation",
  "startTime": "2026-10-13T14:00:00.000Z",
  "customer": {
    "name": "Mira CRM Course",
    "email": "[email protected]"
  }
}

Complete request metadata and observed response. Replace recorded IDs with your own returned values before running.

10. Reschedule, cancel and inspect the retained record

For a staff change, address the reservation by its saved ID. Reschedule takes the new start time and a reason; notify false makes this change quiet. The response has the changed times and reschedule history. Cancel records a reason and retains the reservation with cancelled status. Our final Repository readback confirms those values. The reservation get route is a customer read. A staff token returned forbidden there; staff can inspect the record through their authorized Repository read. Use the customer session when building that customer view. Customer management by email and booking code is a separate contract; the companion lists its routes and identity requirements. Cancellation and deletion are different operations.

Use the staff ID route for the shown reschedule and cancellation. Supply an available future instant and a reason; notify: false was used for rescheduling. Inspect returned start/end times and rescheduleHistory. Cancellation retains the reservation with cancelled status and a reason. Authorized staff Repository readback then verifies the exact record.

GET /crm/reservations/get/:id is a customer read route. Sending the staff token returned 403 as an expected identity boundary, not a broken staff API. A signed-in customer must use the customer session required by that route. Separate email/booking-code management routes exist; POST /crm/reservations/reschedule/:email/:code is an alternative contract, not the staff-ID example. Its configured access requirements still apply; a known email alone is not a staff session.

Reschedule, cancel and inspect the retained record — request or observed-response teaching frame

Reschedule, cancel and inspect the retained record — request or observed-response teaching frame

Reschedule

POST /crm/reservations/reschedule/6aca830537ff863efff476c2 · Identity: staff · Observed HTTP 201.

{
  "startTime": "2026-10-13T14:30:00.000Z",
  "reason": "Course change example",
  "notify": false
}

Complete request metadata and observed response. Replace recorded IDs with your own returned values before running.

Cancel

POST /crm/reservations/cancel/6aca830537ff863efff476c2 · Identity: staff · Observed HTTP 201.

{
  "reason": "Controlled API lesson completed"
}

Complete request metadata and observed response. Replace recorded IDs with your own returned values before running.

Reservation record

GET /repository/get/reservation/6aca830537ff863efff476c2 · Identity: staff · Observed HTTP 200.

Complete request metadata and observed response. Replace recorded IDs with your own returned values before running.

Booking readback

GET /crm/reservations/get/6aca830537ff863efff476c2 · Identity: staff · Observed HTTP 403.

Complete request metadata and observed response. Replace recorded IDs with your own returned values before running.

11. Define the intake schema and access mode

A CRM form defines what a visitor is allowed to submit. This example has its own schema rather than a bound collection: full name, email and request are required. The form's machine name identifies its public route. Reading that route returns the schema and access information your interface needs. We deliberately use an open form here. An email entered into that form is contact information, not proof of a signed-in identity. For restricted forms, configure the intended customer, access-code or invitation policy and carry that proof on both reading and submission. Those alternatives are documented in the companion.

The complete crm_form record below supplies the route name, active status, open authentication mode, inline JSON schema and submit message. Use a unique form name. Reading /client/forms/YOUR_NAME returns its schema and access requirements. The three required fields are fullName, email and request. This example saves a form_submission; it is not configured to create a lead, ticket or workflow automatically.

For restricted access, POST /client/forms/:name/access accepts an email and can send an access message. A subsequent request must carry the form's x-form-code or x-form-token as applicable. Do not merely change the schema or send an email field and claim verified identity. Restricted invitation delivery was not executed here. Appmint Forms covers the form editor and its destination configuration; Workflow APIs covers configured process handling.

Define the intake schema and access mode — request or observed-response teaching frame

Define the intake schema and access mode — request or observed-response teaching frame

Create form

PUT /repository/create · Identity: staff · Observed HTTP 200.

{
  "datatype": "crm_form",
  "isNew": true,
  "data": {
    "name": "crm-intake-1010",
    "title": "CRM API Intake",
    "status": "active",
    "authenticationType": "none",
    "schema": {
      "type": "object",
      "properties": {
        "fullName": {
          "type": "string",
          "title": "Full name"
        },
        "email": {
          "type": "string",
          "format": "email",
          "title": "Email"
        },
        "request": {
          "type": "string",
          "title": "Request"
        }
      },
      "required": [
        "fullName",
        "email",
        "request"
      ]
    },
    "submitMessage": "Your request was saved"
  }
}

Complete request metadata and observed response. Replace recorded IDs with your own returned values before running.

Public form

GET /client/forms/crm-intake-1010 · Identity: public (orgid only) · Observed HTTP 200.

Complete request metadata and observed response. Replace recorded IDs with your own returned values before running.

12. Validate and read back a form submission

Submit answers using the schema's exact field names. Leaving request out returns a required-field error. With all three fields supplied, AppEngine saves a form submission. Read its values, form ID and authentication result: this open submission explicitly says verified false. Staff can retrieve submissions by the form name with pagination. A collection-bound form saves its answers into that collection instead, so follow the configured destination. Workflow is another configuration choice; this unbound form example proves validation and saved answers, not a separately configured approval process.

First omit the request field deliberately. The observed response is a required-field error; display it beside the corresponding field and preserve the user's entered values. Then send all three fields. Save the submission ID and inspect data.values. Authentication metadata records open admission and verified: false: an email-shaped value is not a verified account identity. Staff can page through submissions filtered by formId. A saved unbound form does not establish that a destination process or approval ran.

Validate and read back a form submission — request or observed-response teaching frame

Validate and read back a form submission — request or observed-response teaching frame

Form required

POST /client/forms/crm-intake-1010/submit · Identity: public (orgid only) · Observed HTTP 400.

{
  "fullName": "Mira CRM Course",
  "email": "[email protected]"
}

Complete request metadata and observed response. Replace recorded IDs with your own returned values before running.

Form submit

POST /client/forms/crm-intake-1010/submit · Identity: public (orgid only) · Observed HTTP 201.

{
  "fullName": "Mira CRM Course",
  "email": "[email protected]",
  "request": "Send the developer guide"
}

Complete request metadata and observed response. Replace recorded IDs with your own returned values before running.

Form submissions

GET /crm/forms/submissions?formId=crm-intake-1010&page=1&pageSize=10 · Identity: staff · Observed HTTP 200.

Complete request metadata and observed response. Replace recorded IDs with your own returned values before running.

13. Create a ticket as a signed-in customer

Support intake uses a different identity. Here we reuse the training customer's session from API Foundations and submit a title and description inside data. To make the boundary visible, this request also tries to set done and urgent. The saved ticket is new with medium priority: customer intake does not accept those staff controls. Its reporter comes from the authenticated customer. Reading tickets with that same session and the returned ticket number retrieves this request. For attachments, the multipart create-ticket route accepts the ticket JSON and files together; File Storage APIs explains file paths and upload handling.

Use the existing customer session for /crm/tickets/create. The demonstration deliberately attempts to set done status and urgent priority; the returned ticket instead has new status and medium priority, with reporting identity taken from the authenticated customer. Treat this as proof for this route and payload, not a universal ownership assertion about every API. The same customer successfully read the returned ticket number. This support example uses the separate existing customer from API Foundations, not the email used by the lead/booking example.

For a staff-created ticket with files, the documented alternative is POST /crm/tickets/create-ticket using multipart form data. The data part contains a JSON string of the ticket payload or full envelope; file parts contain actual bytes. Use FormData in JavaScript, files= with Python requests, -F in curl, or Postman form-data. Let the client set the multipart boundary; do not force Content-Type: application/json. This attachment contract was reviewed, not uploaded in this CRM rehearsal. File Storage APIs teaches storage uploads and file references.

Create a ticket as a signed-in customer — request or observed-response teaching frame

Create a ticket as a signed-in customer — request or observed-response teaching frame

Customer ticket

POST /crm/tickets/create · Identity: customer · Observed HTTP 201.

{
  "data": {
    "title": "CRM API course request",
    "description": "Please confirm the integration guide",
    "status": "done",
    "priority": "urgent"
  }
}

Complete request metadata and observed response. Replace recorded IDs with your own returned values before running.

Customer ticket read

GET /crm/tickets/get/Q3CHB319 · Identity: customer · Observed HTTP 200.

Complete request metadata and observed response. Replace recorded IDs with your own returned values before running.

14. Add a staff note, assign and resolve

Switch back to staff credentials to work the ticket. An internal comment is separate from a customer reply. Assignment uses assignTo, and the status action accepts resolved with a resolution summary. Read the ticket again: its stored status is done and the resolution identifies the staff member. The workflow endpoint places the same ticket in Resolved. The reply and resend actions are for external delivery, so they are not executed here. When using them, inspect the saved message and delivery status; a queued message or successful request alone does not prove the customer received it.

Switch back to the staff session. Add an internal comment, assign with assignTo, and resolve using the status endpoint plus a resolution summary. The recorded response and Repository readback contain done status and resolution metadata. Read the ticket workflow as well: the observed stage is Resolved and final. Comments and messages are different collections. Internal notes are not proof of an outgoing reply. Reading messages here did not send, resend or notify a customer. Before integrating an actual reply, configure a delivery channel and verify recipient-visible delivery independently.

Add a staff note, assign and resolve — request or observed-response teaching frame

Add a staff note, assign and resolve — request or observed-response teaching frame

Ticket note

POST /crm/tickets/comments/6aca833237ff863efff476ce · Identity: staff · Observed HTTP 201.

{
  "message": "Checked the API guide for this request"
}

Complete request metadata and observed response. Replace recorded IDs with your own returned values before running.

Ticket assign

PATCH /crm/tickets/assign/6aca833237ff863efff476ce · Identity: staff · Observed HTTP 200.

{
  "assignTo": "[email protected]",
  "note": "Course reviewer"
}

Complete request metadata and observed response. Replace recorded IDs with your own returned values before running.

Ticket resolve

PATCH /crm/tickets/status/6aca833237ff863efff476ce · Identity: staff · Observed HTTP 200.

{
  "status": "resolved",
  "resolution": {
    "summary": "Developer guide confirmed"
  }
}

Complete request metadata and observed response. Replace recorded IDs with your own returned values before running.

Ticket record

GET /repository/get/ticket/6aca833237ff863efff476ce · Identity: staff · Observed HTTP 200.

Complete request metadata and observed response. Replace recorded IDs with your own returned values before running.

Ticket workflow

GET /crm/tickets/workflow/6aca833237ff863efff476ce · Identity: staff · Observed HTTP 200.

Complete request metadata and observed response. Replace recorded IDs with your own returned values before running.

Ticket comments

GET /crm/tickets/comments/6aca833237ff863efff476ce · Identity: staff · Observed HTTP 200.

Complete request metadata and observed response. Replace recorded IDs with your own returned values before running.

Ticket messages

GET /crm/tickets/messages/6aca833237ff863efff476ce · Identity: staff · Observed HTTP 200.

Complete request metadata and observed response. Replace recorded IDs with your own returned values before running.

15. Read customer context and pipeline measurements

Customer three sixty gathers related information for staff. Query by the customer's email, then inspect who matched and the separate result groups. Our CRM customer has the cancelled reservation and related message records; the support example used a different authenticated customer, so its ticket does not belong in this person's count. Keep that identity distinction in your own interface. Pipeline forecast is another read model. The example returns a converted deal, but a tiny training dataset is not a useful business prediction. Use the response structure and filters; do not turn these fixture totals into a performance claim.

Customer 360 takes an email query and assembles matching identities and related sections. Inspect who as well as aggregate counts. In this recording the lead/booking identity has a cancelled reservation; the support ticket belongs to a different customer and is therefore not expected in this person's ticket count. Do not merge two people merely because both examples use a similar first name. Empty sections mean no matching records in this result, not that the platform lacks those modules. Forecast results describe leads within the selected pipeline and range, not settled invoices.

Read customer context and pipeline measurements — request or observed-response teaching frame

Read customer context and pipeline measurements — request or observed-response teaching frame

Customer360

GET /crm/customer-360?email=mira.crm1010%40example.invalid · Identity: staff · Observed HTTP 200.

Complete request metadata and observed response. Replace recorded IDs with your own returned values before running.

Forecast

GET /crm/leads/forecast?pipelineId=6aca82e237ff863efff476aa&months=3 · Identity: staff · Observed HTTP 200.

Complete request metadata and observed response. Replace recorded IDs with your own returned values before running.

16. Choose the next CRM capability deliberately

The same platform extends beyond these core actions. Lead import maps rows into a chosen pipeline; enrichment needs provider configuration and credit. Signed documents add participants, signature positions and a signing lifecycle to a stored PDF. Merchant customer accounts add credit terms, charges and invoicing. Shared accounts connect several people to one account. These are separate capabilities with their own setup and side effects, not extra fields to add to this booking request. The companion maps their contracts and links the relevant course material. No signature invitation, charge, enrichment purchase or external message was executed in this lesson.

Choose the next CRM capability deliberately — concept illustration

Choose the next CRM capability deliberately — concept illustration

Lead import, enrichment and pipeline rules

documented alternatives; external enrichment was not purchased or executed. POST /crm/leads/import accepts this shape with your saved pipeline ID:

{
  "pipelineId": "YOUR_PIPELINE_ID",
  "stageId": "new",
  "rows": [
    {"First Name": "Mira", "Last Name": "Example", "Email": "YOUR_CONTROLLED_EMAIL", "Company": "Training"}
  ]
}

The current implementation recognizes common spreadsheet/export column headings, limits one import to 5,000 rows, and skips duplicate emails within the chosen pipeline or incoming file. Read the returned import result and inspect representative saved leads; do not infer that every row was imported from an HTTP success alone. Use POST /crm/leads/enrich/:id or /enrich/batch with {"leadIds":["YOUR_LEAD_ID"]} only after configuring your enrichment provider and credit. GET /crm/leads/enrichment/status/:id provides the follow-up read. Import and enrichment are different operations.

Pipeline stage IDs, assignment rules and scoring rules belong to pipeline configuration. Read GET /crm/leads/pipelines/:id before changing it with PUT /crm/leads/pipelines/:id; preserve existing fields deliberately and inspect the effect on a training lead. Appmint Leads covers the user-facing pipeline controls. Appmint Automation covers configured automation when available; its written status is separate from a produced video.

Reservation alternatives

A definition may also describe hosts, locations, blocked times and other service constraints. Configure these through Reservations, then inspect the resulting reservation_definition and query /crm/reservations/slots and /crm/reservations/availability with that exact definition. This course provides the complete minimal active definition; it does not claim a multi-host rota was tested. Preserve timezone semantics across daylight-saving changes. Use an ISO instant from the actual slot response rather than appending a guessed offset to a calendar label. Customer email/booking-code rescheduling and cancellation are separate contracts from staff-ID actions, and their current authentication requirements still apply.

Restricted forms and configured destinations

POST /client/forms/:name/access takes {"email":"YOUR_CONTROLLED_EMAIL"} and may deliver an access code or link. Submit the resulting x-form-code or x-form-token on the form's read/submit requests when required. Invitation issuance was not executed here. For a process-bound form, first complete destination/process configuration in Forms and Workflow APIs. Then require both the saved submission and the intended process result as acceptance evidence; an open-form success does not prove that binding.

Signed documents

Upload a real PDF first using File Storage APIs, retaining its storage path. Staff POST /crm/signed-documents creates a draft envelope. Its body supplies title, files: [{path,name}], named participants: [{name,email,role,order}], optional signing order/expiry/context, and signatureFields. A signature field uses fileIndex, PDF page, x, y, width, height, field type and participant assignment. Coordinates are PDF points from the bottom-left of the page. Use the actual PDF geometry; CSS pixels from a scaled browser preview are not interchangeable.

Read GET /crm/signed-documents/:id to inspect normalized participant IDs and spots. While still draft, use POST /:id/update to associate the actual participant with its fields. Preview every page/assignment before POST /:id/send; that action can send invitations. Participants use their own portal token. POST /crm/signed-documents/portal/:token/sign takes consent and signature values:

{
  "consent": true,
  "signature": {"typedName": "YOUR_SIGNER_NAME"},
  "spots": {"YOUR_TEXT_SPOT_ID": {"text": "Agreed value"}}
}

This is a contract reference, not an executed signature. The staff lifecycle includes /:id/remind, /:id/cancel, /:id/void and /:id/retry-merge; select the action appropriate to the current state. Verify participant completion, audit evidence and the resulting signed PDF, including merge failure recovery. Sending an invitation does not complete signing. The AppEngine API reference exposes the Signed Documents route group. This CRM lesson does not provide a separately completed signature video.

Merchant customers and shared accounts

Merchant account billing is distinct from an ordinary customer contact. Staff routes are under /crm/merchant-customers. Create/configure the account via POST /detail and read its saved detail before approval. POST /approve/:id accepts optional notes; suspend/close require a reason. Terms and spending controls must be configured for that account before billing. POST /charge/:id accepts {id,type,amount,reference?,description?}. POST /credit/:id accepts {amount,reason}. These change financial state; they were not executed. POST /invoices/generate/:id and /invoices/send/:id/:invoiceId are separate actions—generation is not recipient delivery. Use Merchant Accounts and the current API reference for account-specific requirements.

Shared accounts assemble multiple customer memberships under one account. Staff can inspect GET /crm/shared-accounts, /:accountId, /:accountId/orders and /:accountId/activity. POST /:accountId/members links an existing customer or invites an email; PUT /:accountId/members/:associationId changes a membership; DELETE unlinks it without deleting previous orders. Adding an invitation can deliver an email and was not executed. Customer-facing management lives under /client-data/shared-accounts/* with its own manager checks. CRM General: Shared Accounts explains membership setup. Read the exact membership and resulting access rather than assuming a shared email gives access.

17. Find the same CRM records in Appmint Studio

The API records also appear in Appmint Studio. Open Leads, find Mira CRM Course, and inspect the saved pipeline, owner and activity. Then open Reservations and find the API consultation: the booking remains visible with its cancelled status. These are the same records returned by the requests, not a separate demonstration database. Use their identifiers and saved values when checking your own application's result.

Open Appmint Studio → Leads. Select the CRM API course pipeline, find Mira CRM Course and open its details. Compare the saved stage and owner, then select Activity to inspect qualification/conversion entries. Open Reservations and find the API consultation in the Cancelled column. Compare its identity and timestamp with the API response. This is an actual app recording of the same saved records. No separate mock database or generated interface is substituted.

Find the same CRM records in Appmint Studio — actual Appmint Studio capture

Find the same CRM records in Appmint Studio — actual Appmint Studio capture

18. Connect each CRM action to its actual result

You can now connect your own application to AppEngine's lead, booking, form and support capabilities. Start with the built-in action, provide the required configuration and caller identity, then inspect the exact saved outcome. Keep queued delivery separate from confirmed delivery, and keep a rejected operation visible instead of silently replacing it with a data write. The written course includes the complete requests, repeatable setup, advanced alternatives and the observed limitations. Each chapter is available independently when you need one operation again.

Completion check: you can create and inspect a pipeline lead, perform its configured qualification/conversion, define and book an available service, handle a refused slot, reschedule and cancel the booking, validate and save an intake submission, and create/read/resolve a support ticket with the correct identity. You can find the resulting lead and reservation in Studio. External notification delivery, signature execution, merchant billing and paid enrichment remain separate, explicitly unexecuted alternatives.

Connect each CRM action to its actual result — concept illustration

Connect each CRM action to its actual result — concept illustration

Verification scope and recovery

Forty-four API evidence files cover the controlled core sequence; the two staff-token calls to the customer reservation-read route deliberately failed. Staff authorized Repository readback succeeded. Real Studio capture verifies the lead and cancelled booking. Six client formats are equivalent examples, not six separately executed end-to-end tests. Restricted form delivery, actual support reply, attachment upload through the ticket alternative, paid enrichment, electronic signing and merchant billing were not executed in this CRM course.

If a request fails, first compare route identity and organization, then the returned configuration IDs and required fields. Refresh availability for a slot refusal. Preserve form input on validation failure. Inspect exact saved IDs before retrying a mutation; blind repeated creates can produce duplicate records. Do not replace an action that refused with a raw write just to display success.

Limitations and checks

Current source reviewed: AppEngine src/crm/leads.controller.ts, leads.service.ts, reservations.controller.ts, reservations.service.ts, crm-form.controller.ts, crm-form-client.controller.ts, form services, ticket controller/service, customer360, signed-document controller/service, merchant-customer controller/service and shared-accounts controller. Actual contracts are preserved in the linked sanitized lab files. This guide supersedes the previous Northside Physio script and its unsupported delivery and identity claims.