NAV Symbol
Logo green
shell ruby

Introduction

Welcome to the WeTeachMe API! You can use our API to access WeTeachMe API endpoints. (Do you know that you can embed the checkout widget using JavaScript? View Docs)

The API has two families of endpoints, and they use different formats.

Products and Orders follow JSON API v1.0. Send and receive application/vnd.api+json, with bodies wrapped in data, type, and attributes.

External integration endpoints (external products, dates and tickets, bookings, transfers, refunds) use plain JSON. Send Content-Type: application/json with a flat request body as shown in each section, and expect a flat JSON response. They do not use the JSON API envelope.

Code examples are provided in Shell and Ruby. You can view them in the dark area to the right, and you can switch the programming language of the examples with the tabs in the top right.

This API documentation page was created with Slate.

Authentication

To authorize, use this code:

# With shell, you can just pass the correct header with each request
curl "api_endpoint_here"
  -H "API-KEY: meowmeowmeow"

# OR

curl "api_endpoint_here?api-key=meowmeowmeow"
require 'httparty'

api = HTTParty.get('some_endpoint_here', headers: { 'API-KEY' => 'meowmeowmeow' })

# OR

api = HTTParty.get('some_endpoint_here?api-key=meowmeowmeow')

Make sure to replace meowmeowmeow with your API key.

WeTeachMe uses API keys to allow access to the API. You can get the API key under your account settings.

API key is expected to be included in all API requests to the server in a header that looks like the following:

API-KEY: meowmeowmeow

A missing or unknown API key returns 404 with an empty body.

Products (Listings)

READ All Products (Listings)

curl 
  -H "Accept: application/vnd.api+json" 
  -H "Content-Type: application/vnd.api+json"
  "https://api.weteachme.com/vendors/products?page[number]=1&page[size]=15"
  -H "API-KEY: meowmeowmeow"
require 'httparty'

HTTParty.get(
  "https://api.weteachme.com/vendors/products?page[number]=1&page[size]=15", 
  headers: {
    "Accept" => 'application/vnd.api+json', 
    "Content-Type" => 'application/vnd.api+json'
    "API-KEY" => 'meowmeowmeow'
  }
);

The above command returns JSON structured like this:

{
  "data":[{
    "id":"1234567",
    "type":"products",
    "links":{
      "self":"http://api.weteachme.com:3000/vendors/products/1234567"
    },
    "attributes":{
      "name":"One On One Programme: Ages 5 to 20",
      "subheading":"Personalise Your Training Plan",
      "description":"</p>We provide feedback, perspective and encouragement to&nbsp;really develop new skills and habits to improve the young player's game.</p>",
      "location-name":"41-43 Stewart St, Richmond VIC 3121, Australia",
      "url":"https://weteachme.com/1111111/1234567-one-on-one-programme-ages-5-to-20",
      "image":{
        "original": "https://image.com/uploads/image/url/1234567/original_22869cb0-84f9-0133-03e6-02b0fd6e4061.jpg",
        "large": "https://image.com/uploads/image/url/1234567/large_22869cb0-84f9-0133-03e6-02b0fd6e4061.jpg",
        "medium": "https://image.com/uploads/image/url/1234567/medium_22869cb0-84f9-0133-03e6-02b0fd6e4061.jpg",
      },
      "status":"active",
      "private":false,
      "things-to-bring":["Your enthusiasm!"],
      "things-to-get":["Book 2 blocks of 5 sessions and receive a 5% discount!","Expert tuition from an experienced teacher!"],
      "things-to-learn":["Work at developing the latent soccer abilities of the young player and maximise player development. ","Coaching is targeted and focused on the individual's needs and goals are set for that young player. ","Individual basis to accelerate growth as a soccer player. "],
      "target-audience":"5-20 years old",
      "dress-code":"Sportswear",
      "dates":[
        {
          "id":136172,
          "start-datetime":"2017-02-04T10:30:00.000Z",
          "end-datetime":"2017-02-05T12:30:00.000Z",
          "capacity":12,
          "sold":6,
          "tickets-left":6,
          "session-number":2,
          "session-name":"Week",
          "schedules":[
            { "start-datetime":"2017-02-04T10:30:00.000Z", "end-datetime":"2017-02-04T12:30:00.000Z" },
            { "start-datetime":"2017-02-05T10:30:00.000Z", "end-datetime":"2017-02-05T12:30:00.000Z" }
          ],
          "tickets":[
            {
              "id":219222,
              "name":"Single",
              "number-of-tickets":12,
              "price":"206.0",
              "member-price":"200.0",
              "sold":5,
              "tickets-left":6,
              "start-datetime":"1743-04-22T10:30:00.000Z",
              "end-datetime":"2290-11-20T10:30:00.000Z",
              "is-private":false,
              "min":1,
              "max":null
            },
            {
              "id":219221,
              "name":"VIP",
              "number-of-tickets":12,
              "price":"306.0",
              "member-price":null,
              "sold":1,
              "tickets-left":6,
              "start-datetime":"1743-04-22T10:30:00.000Z",
              "end-datetime":"2290-11-20T10:30:00.000Z",
              "is-private":true,
              "min":1,
              "max":null
            }
          ]
        }
      ]
    }
  }],
  "links":{
    "first":"http://api.weteachme.com:3000/vendors/products?page%5Bnumber%5D=1&page%5Bsize%5D=15",
    "next":"http://api.weteachme.com:3000/vendors/products?page%5Bnumber%5D=2&page%5Bsize%5D=15",
    "last":"http://api.weteachme.com:3000/vendors/products?page%5Bnumber%5D=16&page%5Bsize%5D=15"
  }
}

This endpoint retrieves all listings.

HTTP Request

GET https://api.weteachme.com/vendors/products

Query Parameters

Parameter Required Default Description
page Pagination (http://jsonapi.org/format/#fetching-pagination)
filter Filtering (http://jsonapi.org/format/#fetching-filtering)

Pagination Fields

Parameter Description
size Default 15, Maximum 45
number Default 1

Filtering Fields

Parameter Type Description
all boolean Returns all records
name string Filter by Listing Name
location-name string Filter by Listing Location
tag-name string Filter by Listing Tag Name
tag-id number Filter by Listing Tag Id
private boolean Filter by Private or Public Listing

READ Product (Listing)

curl 
  -H "Accept: application/vnd.api+json" 
  -H "Content-Type: application/vnd.api+json"
  "https://api.weteachme.com/vendors/products/1234567"
  -H "API-KEY: meowmeowmeow"
require 'httparty'

HTTParty.get(
  "https://api.weteachme.com/vendors/products/1234567", 
  headers: {
    "Accept" => 'application/vnd.api+json', 
    "Content-Type" => 'application/vnd.api+json'
    "API-KEY" => meowmeowmeow"
  }
);

The above command returns JSON structured like this:

{
  "id":"1234567",
  "type":"products",
  "links":{
    "self":"http://api.weteachme.com:3000/vendors/products/1010225"
  },
  "attributes":{
    "name":"One On One Programme: Ages 5 to 20",
    "subheading":"Personalise Your Training Plan",
    "description":"</p>We provide feedback, perspective and encouragement to&nbsp;really develop new skills and habits to improve the young player's game.</p>",
    "location-name":"41-43 Stewart St, Richmond VIC 3121, Australia",
    "url":"https://weteachme.com/1111111/1234567-one-on-one-programme-ages-5-to-20",
    "image":{
      "original": "https://image.com/uploads/image/url/1234567/original_22869cb0-84f9-0133-03e6-02b0fd6e4061.jpg",
      "large": "https://image.com/uploads/image/url/1234567/large_22869cb0-84f9-0133-03e6-02b0fd6e4061.jpg",
      "medium": "https://image.com/uploads/image/url/1234567/medium_22869cb0-84f9-0133-03e6-02b0fd6e4061.jpg",
    },
    "status":"active",
    "private":false,
    "things-to-bring":["Your enthusiasm!"],
    "things-to-get":["Expert tuition from an experienced teacher!", "Skill Upgrades"],
    "things-to-learn":["Work at developing the latent abilities of the young player and maximise player development. ","Coaching is targeted and focused on the individual's needs and goals are set for that young player. "],
    "target-audience":"5-20 years old",
    "dress-code":"Sportswear",
    "dates":[
      {
        "id":136172,
        "start-datetime":"2017-02-04T10:30:00.000Z",
        "end-datetime":"2017-02-04T12:30:00.000Z",
        "capacity":12,
        "sold":6,
        "tickets-left":6,
        "schedules":[
          { "start-datetime":"2017-02-04T10:30:00.000Z", "end-datetime":"2017-02-04T12:30:00.000Z" },
          { "start-datetime":"2017-02-05T10:30:00.000Z", "end-datetime":"2017-02-05T12:30:00.000Z" }
        ],
        "tickets":[
          {
            "id":219222,
            "name":"Single",
            "number-of-tickets":12,
            "price":"206.0",
            "member-price":"200.0",
            "sold":5,
            "tickets-left":6,
            "start-datetime":"1743-04-22T10:30:00.000Z",
            "end-datetime":"2290-11-20T10:30:00.000Z",
            "is-private":false,
            "min":1,
            "max":null
          },
          {
            "id":219221,
            "name":"VIP",
            "number-of-tickets":12,
            "price":"306.0",
            "member-price":null,
            "sold":1,
            "tickets-left":6,
            "start-datetime":"1743-04-22T10:30:00.000Z",
            "end-datetime":"2290-11-20T10:30:00.000Z",
            "is-private":true,
            "min":1,
            "max":null
          }
        ]
      }
    ]
  }
}

This endpoint retrieves all classes.

HTTP Request

GET https://api.weteachme.com/vendors/products/1234567

Orders

Create Order

curl 
  -X POST
  -H "Accept: application/vnd.api+json" 
  -H "Content-Type: application/vnd.api+json"
  -H "API-KEY: meowmeowmeow"
  -d '{ "data": { "type": "vendors/orders", "attributes": { "ticket": { "id": 1234, "qty": 2 } } } }
  "https://api.weteachme.com/vendors/orders"
require 'httparty'

HTTParty.post(
  "https://api.weteachme.com/vendors/orders",
  body: {
    "data": {
      "type": "vendors/orders",
      "attributes": {
        "ticket": {
          "id": 1234,
          "qty": 2 
          }
        }
      }
    }
  }.to_json,
  headers: {
    "Accept" => 'application/vnd.api+json', 
    "Content-Type" => 'application/vnd.api+json'
    "API-KEY" => meowmeowmeow"
  }
);

The above command returns JSON structured like this:

{
  "id":"1234567",
  "type":"vendors/orders",
  "attributes":{
    "url": "https://booking.weteachme.com/checkout?token=3a25g5AU2sxpoxhAXjESzpvBBKxr",
  }
}

This endpoint generates a new order and returns the url to redirect to.

HTTP Request

POST https://api.weteachme.com/vendors/orders

JSON BODY

key Required Default Description
ticket true contains ticket id and qty

External products

Use these endpoints to create or update a listing from an external catalog and to read its current WTM status. The API-KEY determines the vendor; identifiers never grant access to another vendor’s listings.

Create or update an external product

curl "https://api.weteachme.com/vendors/external_products" \
  -X POST \
  -H "Content-Type: application/json" \
  -H "API-KEY: meowmeowmeow" \
  -d '{
    "external_product_id": "course-555",
    "title": "Cooking Workshop",
    "subheading": "Learn to cook delicious meals",
    "description": "In this workshop, you will learn...",
    "audience": "Adults",
    "private": false,
    "skill_level": "Beginner",
    "location": {
      "venue": "Main Kitchen",
      "address": "123 Kitchen Street",
      "timezone": "Australia/Melbourne",
      "city": "Melbourne",
      "state": "Victoria",
      "country": "Australia",
      "postal_code": "3000"
    },
    "image_url": "https://example.com/cooking-workshop.jpg",
    "image_alt": "Students preparing a meal",
    "learn_list": ["Kitchen skills", "Recipe techniques"],
    "get_list": ["Ingredients", "Recipe notes"],
    "bring_list": ["Apron"],
    "dress_code": "Closed-toe shoes"
  }'
require "httparty"

HTTParty.post(
  "https://api.weteachme.com/vendors/external_products",
  headers: { "Content-Type" => "application/json", "API-KEY" => "meowmeowmeow" },
  body: {
    external_product_id: "course-555",
    title: "Cooking Workshop",
    subheading: "Learn to cook delicious meals",
    description: "In this workshop, you will learn...",
    audience: "Adults",
    private: false,
    skill_level: "Beginner",
    location: {
      venue: "Main Kitchen",
      address: "123 Kitchen Street",
      timezone: "Australia/Melbourne",
      city: "Melbourne",
      state: "Victoria",
      country: "Australia",
      postal_code: "3000"
    },
    image_url: "https://example.com/cooking-workshop.jpg",
    image_alt: "Students preparing a meal"
  }.to_json
)

A successful create or update returns 201 Created and the raw serialized listing, which includes many product attributes. Key fields include:

{
  "id": 1234567,
  "name": "Cooking Workshop",
  "status": "under_review",
  "external_product_id": "course-555"
}

external_product_id is the recommended stable identifier. A request with a matching vendor-owned external ID updates that listing; otherwise it creates a listing in under_review. For an existing WTM listing, omit external_product_id and supply its internal product_id. An explicit product_id that is missing or belongs to another vendor returns 422 and never creates a replacement listing.

Omitted top-level listing fields are left unchanged. If you send any of audience, learn_list, get_list, bring_list, or dress_code, include the complete desired set of those metadata fields because that metadata object is replaced. Send status: "active" to reactivate an archived listing. The optional dates array uses the same format as External dates and tickets.

Product fields

Field Required Description
external_product_id Recommended Stable ID in the external system; used to create or update the vendor’s listing
product_id Alternative Internal WTM listing ID; used only when external_product_id is omitted
title New listings Listing title
subheading No Short listing summary
description No Full listing description; HTML is accepted
status No active reactivates a matching archived listing
audience No Intended audience
private No Whether the listing is private
skill_level No Skill level label
seo_meta_title No SEO title
seo_meta_description No SEO description
image_url No Publicly reachable listing image URL
image_alt No Accessible image description
learn_list No Array of learning outcomes
get_list No Array of inclusions
bring_list No Array of things attendees should bring
dress_code No Clothing or safety guidance
location No Venue object; matching venue and address reuse an existing vendor location
dates No Dates and tickets to synchronize with the listing

Location fields

Field Required Description
venue Yes Venue name
address Yes Street address
lat No Latitude
lng No Longitude
timezone No IANA timezone, for example Australia/Melbourne
city No City
state No State or region
country No Country
postal_code No Postal code
parking No Parking instructions

Read external product status

curl "https://api.weteachme.com/vendors/external_products/course-555" \
  -H "Content-Type: application/json" \
  -H "API-KEY: meowmeowmeow"
require "httparty"

HTTParty.get(
  "https://api.weteachme.com/vendors/external_products/course-555",
  headers: { "Content-Type" => "application/json", "API-KEY" => "meowmeowmeow" }
)

The path identifier is the external product ID, not the WTM product ID.

{
  "status": "active"
}

The endpoint returns 200 for a matching vendor-owned listing and 404 when the external ID is unknown or belongs to another vendor.

Response statuses

Status Meaning
201 Listing created or updated
200 External product status returned
404 External product is not found for the authenticated vendor; empty response body
413 Request exceeds 1 MiB (request_too_large)
422 Product validation fails, or payment credentials are rejected (sensitive_payment_data)
503 Request auditing is unavailable (request_audit_unavailable); no listing mutation is attempted

External dates and tickets

Use this endpoint to add or update scheduled dates and ticket types on an existing vendor listing. Identify the listing by external_product_id or by its internal WTM product_id. Both lookups are restricted to the vendor authenticated by API-KEY.

Synchronize dates and tickets

curl "https://api.weteachme.com/vendors/external_dates_tickets" \
  -X POST \
  -H "Content-Type: application/json" \
  -H "API-KEY: meowmeowmeow" \
  -d '{
    "external_product_id": "course-555",
    "dates": [{
      "external_date_id": "course-555-2026-10-24-10am-1pm",
      "start_date": "2026-10-24",
      "start_time": "10:00 am",
      "end_date": "2026-10-24",
      "end_time": "1:00 pm",
      "capacity": 12,
      "tipping_point": 1,
      "sold": 0,
      "status": "active",
      "is_private": true,
      "tickets": [{
        "name": "General admission",
        "description": "Workshop admission",
        "ticket_type": "full_price",
        "open_difference": -720,
        "close_difference": -24,
        "qty": 12,
        "price": 95.00
      }]
    }]
  }'
require "httparty"

HTTParty.post(
  "https://api.weteachme.com/vendors/external_dates_tickets",
  headers: { "Content-Type" => "application/json", "API-KEY" => "meowmeowmeow" },
  body: {
    external_product_id: "course-555",
    dates: [{
      external_date_id: "course-555-2026-10-24-10am-1pm",
      start_date: "2026-10-24",
      start_time: "10:00 am",
      end_date: "2026-10-24",
      end_time: "1:00 pm",
      capacity: 12,
      tipping_point: 1,
      sold: 0,
      status: "active",
      is_private: true,
      tickets: [{
        name: "General admission",
        description: "Workshop admission",
        ticket_type: "full_price",
        open_difference: -720,
        close_difference: -24,
        qty: 12,
        price: 95.00
      }]
    }]
  }.to_json
)

A successful request returns 201 Created.

{
  "message": "1 dates successfully created"
}

external_date_id is the date idempotency key within the listing. Replaying it updates the existing date instead of creating another one. Tickets on that date are updated by exact ticket name; keep ticket names unchanged between synchronization runs.

Upload reviewable dates with is_private: true. Publish an approved date by replaying the same external_date_id and ticket names with is_private: false.

Top-level fields

Field Required Description
external_product_id One identifier Stable external listing ID
product_id One identifier Internal WTM listing ID; used only when external_product_id is omitted
dates Yes Non-empty array of date objects

Date fields

Field Required Description
external_date_id Recommended Stable external date ID used for updates and safe replays
id-code No External source code stored in WTM as external_code; it is not an update key
start_date Yes Start date in YYYY-MM-DD format
start_time Yes Start time, for example 10:00 am
end_date Yes End date in YYYY-MM-DD format
end_time Yes End time
capacity Yes Total date capacity
tipping_point Yes Minimum attendance threshold
sold Yes Existing sold quantity in the source system
status No Defaults to active
cancelled No Defaults to false
is_private No Hide or publish the date
tickets Yes Non-empty array of ticket objects

Ticket fields

Field Required Description
name Yes Ticket name and update key within the date
description No Ticket description
ticket_type Yes Ticket type, commonly full_price
qty Yes Ticket quantity; 0 uses the date capacity
price Yes Ticket price
open_difference No Booking-open time in hours relative to class start; omitted uses the API default
close_difference No Booking-close time in hours relative to class start; -24 closes 24 hours before
weight No Display order; omitted uses array order
ticket_merchandises No Compulsory merchandise rows containing merchandise_id and qty

Response statuses

Status Meaning
201 Dates and tickets synchronized
413 Request exceeds 1 MiB (request_too_large)
422 Listing/date/ticket validation fails, or payment credentials are rejected (sensitive_payment_data)
503 Request auditing is unavailable (request_audit_unavailable); no synchronization is attempted

The controller currently returns 422 for unexpected internal failures as well as validation failures. In those cases errors may be a bare string; message remains human-readable. A multi-date request is transactional: a validation failure rolls back that synchronization call.

External bookings

Use this endpoint to record a booking that an external service has already confirmed and paid. It creates the WTM order and attendee records but does not collect money or call a payment gateway.

The API-KEY determines the vendor. The request does not accept a vendor or company ID. Obtain the vendor’s internal ticketing_id values from the ticket data returned by the vendor Products API before recording a booking.

Record an external booking

curl "https://api.weteachme.com/vendors/external_bookings" \
  -X POST \
  -H "Content-Type: application/json" \
  -H "API-KEY: meowmeowmeow" \
  -d '{
    "source": "partner_portal",
    "external_booking_id": "booking-123",
    "booked_at": "2026-08-25T10:15:00+10:00",
    "notify_customer_enrolment": true,
    "notify_customer_payment": false,
    "notify_vendor": false,
    "purchaser": {
      "firstname": "Billie",
      "lastname": "Buyer",
      "email": "billie@example.com",
      "mobile": "0400000000",
      "requirements": "Wheelchair access"
    },
    "line_items": [{
      "ticketing_id": 123,
      "unit_price": "55.00",
      "quantity": 2
    }],
    "payment": {
      "amount": "110.00",
      "currency": "AUD",
      "reference": "payment-456"
    }
  }'
require "httparty"

HTTParty.post(
  "https://api.weteachme.com/vendors/external_bookings",
  headers: { "Content-Type" => "application/json", "API-KEY" => "meowmeowmeow" },
  body: {
    source: "partner_portal",
    external_booking_id: "booking-123",
    booked_at: "2026-08-25T10:15:00+10:00",
    notify_customer_enrolment: true,
    notify_customer_payment: false,
    notify_vendor: false,
    purchaser: {
      firstname: "Billie",
      lastname: "Buyer",
      email: "billie@example.com",
      requirements: "Wheelchair access"
    },
    line_items: [{
      ticketing_id: 123,
      unit_price: "55.00",
      quantity: 2
    }],
    payment: { amount: "110.00", currency: "AUD", reference: "payment-456" }
  }.to_json
)

A new booking returns 201 Created.

{
  "order_id": 987,
  "external_booking_id": "booking-123",
  "source": "partner_portal",
  "status": "completed",
  "replayed": false
}

Each line item books seats either by count or by naming each attendee:

At least one of the two is required. When both are present, quantity must equal the number of attendees.

line_items[].unit_price is the actual price paid per seat. Prices and payment.amount must be finite decimal values. The sum of unit_price × seats for all lines must equal payment.amount, and payment.currency must match the vendor’s currency. Ticketings must be active, owned by the authenticated vendor, have enough capacity, and use the full-amount payment option; instalment and recurring ticketings are not supported.

Purchasers accept firstname, lastname, email, mobile, and optional requirements. Other purchaser fields are rejected. Purchaser and attendee emails must be valid email addresses, and mobile numbers must be strings of at most 32 characters. requirements, source, external_booking_id, payment.reference, and every purchaser or attendee firstname, lastname, and email must be strings of at most 255 characters. booked_at must describe a time that has already occurred; future timestamps are rejected.

Request bodies are limited to 1 MiB. Larger requests return 413 with error_code: "request_too_large". Do not send authentication secrets, card details, or bank credentials in the booking body; payment credentials are rejected before booking state is created.

Messages

WTM sends no messages for an external booking unless the request asks for them. All three flags are optional JSON booleans that default to false:

Regardless of these flags, every attendee still gets WTM’s normal post-enrolment setup: class-pass coupons, LMS access for on-demand courses, review invites, and waiting-list removal.

The retired capture_attendee_details, email_opt_out, and notify_customer keys are rejected with 422.

Attendee details

External booking creation never rejects a request because of the vendor’s configured booking questions. Attendee, custom-field, and extra-field validation is bypassed while the booking is created.

After creation, WTM checks each attendee against the vendor’s booking rules: name and email, mobile where the vendor requires it, and every enabled extra field and required custom field for the ticket. Attendees that satisfy every rule are marked complete. Attendees that are missing details stay open, and the attendee confirmation email includes an “Update Attendee Information” link until the purchaser completes them through the WTM attendee page, where the normal configured validations apply.

The order also records registration_context.external_booking with the source, external_booking_id, booked_at, both message flags, and whether attendees were supplied explicitly.

Retry behavior

The combination of the authenticated vendor, source, and external_booking_id is the idempotency key. Retry a timed-out request with the same complete payload. Normalization ignores JSON object key ordering; values and array ordering must remain identical.

Do not reuse an external booking ID for a different booking.

Response statuses

Status Meaning
201 New booking recorded and completed
200 Identical idempotent replay; no duplicate order, attendee, payment, or inventory change
400 Malformed JSON (invalid_json)
404 A ticket does not exist for the authenticated vendor
409 The idempotency key already exists with different business content
413 Request body exceeds 1 MiB
422 Structurally invalid payload or business validation failed, including ticket availability, amount, currency, or prohibited payment credentials
503 The request audit could not be created, so no booking mutation was attempted

External transfers

Use this endpoint to move an attendee to another ticket owned by the same vendor. The transfer is recorded as attendee-initiated and may collect a transfer fee from a reusable card already stored on the order.

Transfer an attendee

curl "https://api.weteachme.com/vendors/external_transfers" \
  -X POST \
  -H "Content-Type: application/json" \
  -H "API-KEY: meowmeowmeow" \
  -d '{
    "order_id": 987,
    "attendee_id": 654,
    "to_product_id": 321,
    "to_teaching_id": 432,
    "to_ticketing_id": 543,
    "subtotal": 10.00,
    "transfer_charge": 10.00
  }'
require "httparty"

HTTParty.post(
  "https://api.weteachme.com/vendors/external_transfers",
  headers: { "Content-Type" => "application/json", "API-KEY" => "meowmeowmeow" },
  body: {
    order_id: 987,
    attendee_id: 654,
    to_product_id: 321,
    to_teaching_id: 432,
    to_ticketing_id: 543,
    subtotal: 10.00,
    transfer_charge: 10.00
  }.to_json
)

A successful transfer returns 201 Created.

{
  "transfer_id": 765,
  "attendee_id": 654,
  "status": "transferred",
  "payment_status": "paid"
}

Request fields

Field Required Description
order_id Yes Existing order containing the attendee
attendee_id Yes Active attendee to transfer
to_product_id Yes Destination WTM listing ID
to_teaching_id Yes Destination date/session ID
to_ticketing_id Yes Destination ticket ID
subtotal No Transfer amount; defaults to 0; a positive value must be at least 2.00 in every currency
transfer_charge No Transfer fee; defaults to 0 and must equal subtotal
charge_now No Boolean override; omitted charges now for a free transfer or when a reusable card exists

payment_status is paid when the fee is settled during the request and pending_payment when the attendee is moved but the fee remains due. If a stored-card charge returns 422 with error_code: "charge_declined", the caller may retry with charge_now: false to complete the held, pay-later transfer.

Status Meaning
201 Attendee transferred; inspect payment_status
404 The order, attendee, destination listing, date, or ticket is missing or belongs to another vendor; empty response body
413 Request exceeds 1 MiB (request_too_large)
422 Invalid amount, same-ticket transfer, past cutoff, unavailable destination, payment failure, or rejected payment credentials (sensitive_payment_data)
503 Request auditing is unavailable (request_audit_unavailable); no transfer is attempted

Successful requests move inventory and send the transfer confirmation asynchronously. This endpoint has no idempotency key; do not blindly replay a request that may already have succeeded.

External refunds

Use this endpoint to execute a refund decision that an external policy service has already approved. It refunds the named payment or issues a credit note, then cancels the named attendee spots and returns inventory.

Execute an approved refund

curl "https://api.weteachme.com/vendors/external_refunds" \
  -X POST \
  -H "Content-Type: application/json" \
  -H "API-KEY: meowmeowmeow" \
  -d '{
    "payment_id": 9497,
    "amount": 55.00,
    "attendee_ids": [8844],
    "refund_reason": "customer_requested",
    "credit_note": false,
    "note": "Approved by the partner cancellation policy"
  }'
require "httparty"

HTTParty.post(
  "https://api.weteachme.com/vendors/external_refunds",
  headers: { "Content-Type" => "application/json", "API-KEY" => "meowmeowmeow" },
  body: {
    payment_id: 9497,
    amount: 55.00,
    attendee_ids: [8844],
    refund_reason: "customer_requested",
    credit_note: false,
    note: "Approved by the partner cancellation policy"
  }.to_json
)

A successful refund returns 201 Created.

{
  "refund_id": 2468,
  "status": "completed"
}

Request fields

Field Required Description
payment_id Yes Payment to refund; must belong to the authenticated vendor
amount Yes Approved refund or credit-note amount; cannot exceed the remaining refundable amount
attendee_ids No Attendees whose spots should be cancelled; each must belong to the payment’s order
refund_reason No Approved reason, for example customer_requested or class_cancelled
credit_note No true issues vendor credit instead of refunding through the payment gateway
expiry_date No Optional credit-note expiry date
note No Internal note recorded with the refund

All named attendees are validated before money moves. An already-cancelled attendee returns 422; a missing or foreign payment/attendee returns 404. A successful request cancels the attendees and decreases the associated ticket sold counts.

Status Meaning
201 Refund or credit note completed
404 Payment or attendee not found within the authenticated vendor’s booking; empty response body
413 Request exceeds 1 MiB (request_too_large)
422 Amount exceeds the refundable balance, an attendee is already cancelled, another refund rule fails, or payment credentials are rejected (sensitive_payment_data)
503 Request auditing is unavailable (request_audit_unavailable); no refund is attempted

This endpoint has no idempotency key. After an ambiguous timeout, verify the payment and attendee state in WTM before deciding whether to retry.

Errors

Products and Orders

These endpoints follow JSON API. Errors come back as a JSON API errors array, each entry carrying status, title, and detail.

External endpoints

The external endpoints return plain JSON. When the endpoint defines an error code, the body is:

{
  "error_code": "invalid_payload",
  "errors": ["quantity must be a positive integer"]
}

Where no code is defined, the body is just errors:

{
  "errors": ["Listing not found for product_id course-555"]
}

errors is always an array of human-readable strings.

HTTP status error_code Endpoints When
400 invalid_json bookings Request body is not valid JSON.
404 none, empty body all API key missing or unknown, or the record does not belong to the authenticated vendor.
404 not_found bookings A ticketing_id in the request was not found.
409 idempotency_conflict bookings Same source + external_booking_id as an earlier booking, but different content.
413 request_too_large all external Request body over 1 MiB.
422 invalid_payload bookings A field is missing, the wrong type, over its limit, or a retired key was sent.
422 booking_invalid bookings The booking failed a business rule, for example a sold-out ticket.
422 charge_declined transfers The stored card was declined for the transfer fee. Retry with charge_now: false.
422 sensitive_payment_data all external Card or bank credentials were found in the body. They are never accepted.
422 none products, dates and tickets, transfers, refunds Validation failed; see errors. Dates and tickets also returns message.
503 request_audit_unavailable all external Request auditing is down. Retry later; nothing was created.