swagger: '2.0'
info:
title: Handy’s Partner Bookings Orders API
description: "<p>The Handy Partner API offers retailers fixed price installation\nservices that can be added to their eCommerce site or in-store checkout experience.\nTo start using the Handy Partner API, contact partners-eng@handy.com in order to\n get access to the Sandbox.</p>\n <p>Once you are ready to move to prod, we will walk you through quick authentication steps.</p>"
version: v1
contact: partnerships@handy.com
host: partners.services.handy.com
schemes:
- https
tags:
- name: Orders
paths:
/api/v1/orders:
post:
summary: Create Order
description: "Creates an order using one or more product SKUs that correspond to Angi services.\n\n ### Multi-Item Orders \n **Multiple items with the same product SKU** - If a customer purchased multiple Angi services with the **same** product SKU in a single basket, **only create one object for this product SKU** in the `products` array and indicate the quantity within the product object in the `product[:order_details][:quantity]` field. \n\n *Example of three products with the same SKU (quantity set on `order_details`).* \n\n ``` \n\n \"products\": [\n {\n \"sku\": \"ANGI1234\",\n \"product_info\": {...},\n \"order_details\": {\n \"quantity\": 3,\n ...\n },\n \"scheduling_details\": {}\n }\n] \n\n``` \n\n **Multiple items with different product SKUs** - If there are multiple product SKUs that correspond to more than one Angi service, create `product` objects for each of the SKUs indicating their associated quantities. \n\n *Example of three products with the same SKU (quantity set on `order_details`) and one product with a different SKU on the same order.* \n\n ``` \n\n \"products\": [\n {\n \"sku\": \"ANGI1234\",\n \"product_info\": {...},\n \"order_details\": {\n \"quantity\": 3,\n ...\n },\n \"scheduling_details\": {}\n },\n {\n \"sku\": \"ANGI5555\",\n \"product_info\": {...},\n \"order_details\": {\n \"quantity\": 1,\n ...\n },\n \"scheduling_details\": {}\n }\n] \n\n``` \n\n \n\n **Do NOT** create product objects for product SKUs that do not map/correspond to an Angi service. \n\n ### Success and Webhooks \n When an order is successfully created you will receive the `201` status code which is an acknowledgment from Angi that your order was created successfully. If your partner configuration has been set up with webhooks, after submitting an order creation you will receive either `booking_confirmed`, `booking_rescheduled`, or both (depending on your configuration). These webhooks will provide you with the most up-to-date booking/scheduling information associated with the submitted order. (See the Webhooks tab for more information around webhooks request body structure and more)."
tags:
- Orders
consumes:
- application/json
produces:
- application/json
parameters:
- name: HDY-PARTNER-ID
in: header
type: string
description: Your assigned partner ID
example: partner_name
- name: HDY-TIMESTAMP
in: header
type: string
description: Seconds since epoch UTC
example: '1525361611'
- name: HDY-SIGNATURE
in: header
type: string
description: 'Signature generated using your private key.
Click <a href=''https://github.com/Handybook/API-Request-Signing'' target=''_blank''>here</a> to learn how.'
example: OF5xI0LbLXHkFBLyNa8l..XHk=
- name: request_body
in: body
schema:
$ref: '#/definitions/order_creation_params'
responses:
'201':
description: Created. Order created with required bookings
schema:
type: object
properties:
order:
$ref: '#/definitions/order_response'
'400':
description: Bad request error, check order details passed in on request.
schema:
type: object
properties:
message:
type: string
example: Invalid user details
code:
type: string
example: user_details_invalid
error_uuid:
$ref: '#/definitions/error_uuid'
more_info:
type: object
example:
email:
- is an invalid
required:
- message
- code
- error_uuid
- more_info
'401':
description: Unauthorized error, check authentication mechanism.
schema:
type: object
properties:
message:
type: string
example: Unauthorized request
code:
type: string
example: unauthorized
error_uuid:
$ref: '#/definitions/error_uuid'
required:
- message
- code
- error_uuid
'422':
description: Unprocessable entity error, check SKU provided on request.
schema:
type: object
properties:
message:
type: string
example: Invalid SKU provided
code:
type: string
example: invalid_sku
error_uuid:
$ref: '#/definitions/error_uuid'
required:
- message
- code
- error_uuid
'409':
description: Conflict error, order has already been processed. No further action required.
schema:
type: object
properties:
message:
type: string
example: Order has already been processed. You do no need to do anything else.
code:
type: string
example: order_already_processed
error_uuid:
$ref: '#/definitions/error_uuid'
required:
- message
- code
- error_uuid
'500':
description: Internal server error, order creation failed.
schema:
type: object
properties:
message:
type: string
example: Failed to create the order
code:
type: string
example: order_creation_failed
error_uuid:
$ref: '#/definitions/error_uuid'
required:
- message
- code
- error_uuid
get:
summary: Recent Orders
description: "Get a list of partner specific orders created in the API. A useful aspect of this endpoint is that you can check the status of an order that has been submitted as well as receiving the status of each individual `order_product` within the parent order. The endpoint is flexible in that you can query based on `order_id` (same as `partner_order_id`), `user_id` (the ID assigned to the consumer upon order creation and returned to the partner in the response), `booking_guid` (the ID assigned to the booking upon order creation and returned to the partner in the response). \n\n### Pagination \n\n The `page` and `size` query parameters can be used to receive paginated responses from the recent orders API. Example scenario: I have 4 orders in total that will be returned by this API call. If I call this endpoint with no query parameters I will receive all 4 orders. But if I want each page to have a size of 2 orders and I want to receive the 2nd page of orders, I would pass in query parameters `?size=2&page=2`. I will receive the last 2 orders in the response."
tags:
- Orders
consumes:
- application/json
produces:
- application/json
parameters:
- name: HDY-PARTNER-ID
in: header
type: string
description: Your assigned partner ID
example: partner_name
- name: HDY-TIMESTAMP
in: header
type: string
description: Seconds since epoch UTC
example: '1525361611'
- name: HDY-SIGNATURE
in: header
type: string
description: 'Signature generated using your private key.
Click <a href=''https://github.com/Handybook/API-Request-Signing'' target=''_blank''>here</a> to learn how.'
example: OF5xI0LbLXHkFBLyNa8l..XHk=
- name: size
in: query
type: integer
description: Number of orders per page (optional, used for pagination. See endpoint description above for more info).
required: false
default: 10
example: 1
- name: page
in: query
type: integer
description: Page number for paginated list of orders (optional, see endpoint description above for more info).
default: 1
example: 2
- name: order_id
in: query
type: string
description: Filter orders by `partner_order_id` that was passed in on the order creation request (optional).
example: '110001023'
- name: user_id
in: query
type: string
description: 'Filter orders by `user_id` (optional). This is the same `user_id` that is returned in the response from order creation. '
example: xzvdfhryhbdbe
- name: booking_guid
in: query
type: string
description: Filter orders by booking_guid (optional). This is the same ID that is returned in the `booking_id` field on order creation.
example: w813v41k6lxzj5g
responses:
'200':
description: OK
schema:
type: object
properties:
orders:
type: array
items:
$ref: '#/definitions/recent_orders_response'
total:
type: integer
description: The number of orders returned in the `orders` response array.
example: 1
'401':
description: Unauthorized error, check authentication mechanism.
schema:
type: object
properties:
message:
type: string
example: Unauthorized request
code:
type: string
example: unauthorized
error_uuid:
$ref: '#/definitions/error_uuid'
required:
- message
- code
- error_uuid
'500':
description: Internal server error.
schema:
type: object
properties:
message:
type: string
example: Internal server error.
code:
type: string
example: order_creation_failed
more_info:
type: string
example: An unexpected error has occurred.
error_uuid:
$ref: '#/definitions/error_uuid'
/api/v1/orders/{partner_order_id}:
get:
summary: Retrieve Order
description: Retrieve information about an order by `partner_order_id`. A useful aspect of this endpoint is that you can check the status of an order that has been submitted as well as receiving the status of each individual `order_product` within the parent order.
tags:
- Orders
consumes:
- application/json
produces:
- application/json
parameters:
- name: HDY-PARTNER-ID
in: header
type: string
description: Your assigned partner ID
example: partner_name
- name: HDY-TIMESTAMP
in: header
type: string
description: Seconds since epoch UTC
example: '1525361611'
- name: HDY-SIGNATURE
in: header
type: string
description: 'Signature generated using your private key.
Click <a href=''https://github.com/Handybook/API-Request-Signing'' target=''_blank''>here</a> to learn how.'
example: OF5xI0LbLXHkFBLyNa8l..XHk=
- name: partner_order_id
in: path
type: string
required: true
description: The `partner_order_id` that was used on order creation.
example: '110001023'
responses:
'200':
description: OK. Order retrieved.
schema:
type: object
properties:
order:
$ref: '#/definitions/retrieve_order_response'
'401':
description: Unauthorized error, check authentication mechanism.
schema:
type: object
properties:
message:
type: string
example: Unauthorized request
code:
type: string
example: unauthorized
error_uuid:
$ref: '#/definitions/error_uuid'
required:
- message
- code
- error_uuid
'404':
description: Not found error.
schema:
type: object
properties:
message:
type: string
example: The order was not found
code:
type: string
example: order_not_found
error_uuid:
$ref: '#/definitions/error_uuid'
required:
- message
- code
- error_uuid
'500':
description: Internal server error.
schema:
type: object
properties:
message:
type: string
example: Internal server error.
code:
type: string
example: order_creation_failed
more_info:
type: string
example: An unexpected error has occurred.
error_uuid:
$ref: '#/definitions/error_uuid'
patch:
summary: Update Order
description: "Update `delivery_date`, `service_date` or `service_datetime` for products or service bookings associated with an order in the `scheduling_details` object. \n\n ### Multi-Item Orders \n\n When an order is originally created with multiple items, items that have a SKU that maps to the same service these will already be created as one booking. In the case of multiple products in the cart with SKUs that map to different services, multiple bookings will have been created. If the multi-item orders have not experienced any consolidation, updates to the order will impact all items in the booking. The update method will not allow for changing properties other than scheduling on the booking. In a case where the customer purchases more products, this should be created as a new order rather than trying to update items on the existing order. \n\n ### Updating Consolidated Orders \n\n When an order is submitted that can been consolidated into an existing booking for a consumer, an update on an individual order will operate on the elements of the booking that are associated with that particular order. For example, if I sent in a `delivery_date` update on the second order that is a week in the future but this order was consolidated into the first booking, a new booking will be created with the elements of the second partner order. (Webhooks will be sent for both partner orders). \n\n ### Success and Webhooks \n\n When an order is successfully updated you will receive the `200` status code which is an acknowledgment from Angi that your order was updated. If your partner configuration has been set up with webhooks, after submitting an order creation you will receive a `booking_rescheduled`. This webhook will provide you with the most up-to-date booking/scheduling information associated with the updated order. (See the Webhooks tab for more information around webhooks request body structure and more)."
tags:
- Orders
consumes:
- application/json
produces:
- application/json
parameters:
- name: HDY-PARTNER-ID
in: header
type: string
description: Your assigned partner ID
example: partner_name
- name: HDY-TIMESTAMP
in: header
type: string
description: Seconds since epoch UTC
example: '1525361611'
- name: HDY-SIGNATURE
in: header
type: string
description: 'Signature generated using your private key.
Click <a href=''https://github.com/Handybook/API-Request-Signing'' target=''_blank''>here</a> to learn how.'
example: OF5xI0LbLXHkFBLyNa8l..XHk=
- name: partner_order_id
in: path
type: string
required: true
description: The partner_order_id that was used to create the order.
example: 110001023xx455
- name: request_body
in: body
schema:
$ref: '#/definitions/update_order_req_body'
required: true
responses:
'200':
description: Order updated successfully.
schema:
type: object
properties:
order:
$ref: '#/definitions/order_response'
'400':
description: Bad request error, check scheduling_details.
schema:
type: object
properties:
message:
type: string
example: requested reschedule time is in the past
code:
type: string
example: reschedule_request_date_in_past
error_uuid:
$ref: '#/definitions/error_uuid'
more_info:
type: object
example:
code: reschedule_request_date_in_past
data:
- detail: requested reschedule time is in the past
required:
- message
- code
- error_uuid
- more_info
'403':
description: Forbidden. Check auth credentials for your partner.
schema:
type: object
properties:
message:
type: string
example: Access denied.
code:
type: string
example: forbidden_access_denied
error_uuid:
$ref: '#/definitions/error_uuid'
more_info:
type: object
example:
code: forbidden_access_denied
data:
- detail: Forbidden.
'404':
description: Order not found, check partner_order_id on request.
schema:
type: object
properties:
message:
type: string
example: 'Order #110001023 was not found'
code:
type: string
example: order_not_found
error_uuid:
$ref: '#/definitions/error_uuid'
more_info:
type: string
example: The order was not found
required:
- message
- code
- error_uuid
- more_info
'422':
description: Unprocessable entity error, check scheduling details.
schema:
type: object
properties:
message:
type: string
example: reschedule requests must be made at least 24 hours before the booking start time
code:
type: string
example: reschedule_request_not_within_reschedule_window
error_uuid:
$ref: '#/definitions/error_uuid'
more_info:
type: object
example:
code: reschedule_request_not_within_reschedule_window
data:
- detail: reschedule requests must be made at least 24 hours before the booking start time
required:
- message
- code
- error_uuid
- more_info
'500':
description: Internal server error. Order NOT updated.
schema:
type: object
properties:
message:
type: string
example: internal server error
code:
type: string
example: internal_server_error
error_uuid:
$ref: '#/definitions/error_uuid'
more_info:
type: object
example:
code: internal_server_error
data:
- detail: internal server error
required:
- message
- code
- error_uuid
- more_info
delete:
summary: Cancel
description: "The endpoint can be used to cancel a order which in turn will cancel bookings associated with the order. \n Endpoint has some restrictions which would mean a booking cannot be cancelled. Some of the restrictions are as follows `order_already_cancelled`, `booking_already_cancelled`, `booking_already_completed`, `booking_already_started`, can see the examples in the `422` block. \n\n **Note** The Api endpoint is a blanket delete call to cancel all bookings associated with an order, continue reading for different scenarios of cancellations. \n\n ### Multi-Product Order \n\n If the order has multiple products, the order might have multiple bookings. A cancellation request for a order that has two products, will result in cancelling of both bookings and a slightly different looking response body with a `200` status code \n\n \n\n ``` {\n \"order\": {\n \"partner_order_id\": \"110001023\",\n \"user_id\": \"ojxyzrldy81gl28\",\n \"address_id\": \"71npkxynjqog2w6\",\n \"products\": [\n {\n \"booking_id\": \"v50r6460w43nd1p\",\n \"charge_amount_in_cents\": 9099,\n \"charge_currency\": \"USD\",\n \"customer_price_in_cents\": null,\n \"default_schedule\": false,\n \"delivery_date\": \"2018-02-05\",\n \"error\": null,\n \"line_number\": \"1\",\n \"name\": \"Shower System 1 Pro Installation\",\n \"product_category\": null,\n \"order_date\": \"2024-06-27T23:01:48.000Z\",\n \"order_product_id\": \"k5o3pmrvq7x4qjg\",\n \"partner_machine_name\": \"partner_name\",\n \"partner_order_id\": \"110001023\",\n \"product_status\": \"cancelled\",\n \"product_weight_in_lbs\": 50,\n \"quantity\": 1,\n \"service_date\": \"2024-06-28\",\n \"service_datetime\": null,\n \"sku\": \"Example1\",\n \"wholesale_price_in_cents\": null,\n \"customer_scheduling_preferences\": null\n },\n {\n \"booking_id\": \"rk3dx4wpy2q76o9\",\n \"charge_amount_in_cents\": 9099,\n \"charge_currency\": \"USD\",\n \"customer_price_in_cents\": null,\n \"default_schedule\": false,\n \"delivery_date\": \"2018-02-05\",\n \"error\": null,\n \"line_number\": \"1\",\n \"name\": \"Shower System 1 Pro Installation\",\n \"product_category\": null,\n \"order_date\": \"2024-06-27T23:01:48.000Z\",\n \"order_product_id\": \"nxr0j1py31eoqpd\",\n \"partner_machine_name\": \"partner_name\",\n \"partner_order_id\": \"110001023\",\n \"product_status\": \"cancelled\",\n \"product_weight_in_lbs\": 50,\n \"quantity\": 1,\n \"service_date\": \"2024-06-28\",\n \"service_datetime\": null,\n \"sku\": \"Example2\",\n \"wholesale_price_in_cents\": null,\n \"customer_scheduling_preferences\": null\n }\n ],\n \"created_at\": \"2024-06-27T23:01:48.000Z\",\n \"processing_status\": \"complete\",\n \"partner_machine_name\": \"partner_name\",\n \"metadata\": {}\n }\n} \n\n``` \n\n When a order contains multiple bookings and one of the bookings is completed or started and a `cancel` request is sent, the api will cancel booking that was not started and return the `booking_id` of the booking that was not cancelled with a `422` status code. \n\n ```\n {\n \"message\": \"Some bookings within partner order 110001023 could not be cancelled: k1vy62j8940mo7e.\",\n \"code\": \"booking_already_completed\",\n \"more_info\": \"Booking has been completed\",\n \"error_uuid\": \"f8db8791a9db3a71e06a5563901291ed\"\n} \n\n``` \n\n ### Success and Webhooks\n\n When an order is successfully cancelled you will receive the 200 status code which is an acknowledgment from Angi that your order was cancelled. If your partner configuration has been set up with webhooks, after order is cancelled you will receive one or more `booking_cancelled` events, depending on how many bookings the order had. This webhook will provide you with the info that has `booking_id`, `partner-order_id` and `event_time`. (See the Webhooks tab for more information around webhooks request body structure and more). "
tags:
- Orders
consumes:
- application/json
produces:
- application/json
parameters:
- name: HDY-PARTNER-ID
in: header
type: string
description: Your assigned partner ID
example: partner_name
- name: HDY-TIMESTAMP
in: header
type: string
description: Seconds since epoch UTC
example: '1525361611'
- name: HDY-SIGNATURE
in: header
type: string
description: 'Signature generated using your private key.
Click <a href=''https://github.com/Handybook/API-Request-Signing'' target=''_blank''>here</a> to learn how.'
example: OF5xI0LbLXHkFBLyNa8l..XHk=
- name: id
in: path
type: string
required: true
description: The Order's id
example: '110001023'
- name: reason
in: query
description: A reason for cancellation (optional). If passing a reason parameter it should be valid else will result in reason_not_found
type: string
example: partner_returned_item
responses:
'200':
description: OK, Order cancelled successfully
schema:
type: object
properties:
order:
$ref: '#/definitions/order_response'
'400':
description: Bad Request, Invalid reason code in query param
schema:
type: object
properties:
message:
type: string
example: Reason not found.
code:
type: string
example: invalid_request_parameter
error_uuid:
$ref: '#/definitions/error_uuid'
required:
- message
- code
- error_uuid
'404':
description: Order Not found
schema:
type: object
properties:
message:
type: string
example: The order was not found
code:
type: string
example: order_not_found
error_uuid:
$ref: '#/definitions/error_uuid'
required:
- message
- code
- error_uuid
'409':
description: Order already cancelled
schema:
type: object
properties:
message:
type: string
example: Order 1234567890 is already cancelled
code:
type: string
example: order_already_cancelled
error_uuid:
$ref: '#/definitions/error_uuid'
required:
- message
- code
- error_uuid
'500':
description: Order Cancellation failed, Internal server error.
schema:
type: object
properties:
message:
type: string
example: Some bookings within partner order 1234567890 could not be cancelled
code:
type: string
example: booking_cancellation_failed
error_uuid:
$ref: '#/definitions/error_uuid'
required:
- message
- code
- error_uuid
'422':
description: Order not cancelled, Booking already completed | Booking Already Cancelled | Booking already started
schema:
type: object
properties:
message:
type: string
example: Order 110001023 is already cancelled.
code:
type: string
example: booking_already_cancelled
error_uuid:
$ref: '#/definitions/error_uuid'
required:
- message
- code
- error_uuid
definitions:
sku:
type: string
example: HANDY1234
description: The product identifier that has been allocated to the product you are submitting on this order request.
order_creation_params:
type: object
properties:
user:
$ref: '#/definitions/user'
address:
$ref: '#/definitions/address'
order:
$ref: '#/definitions/order'
required:
- user
- address
- order
retrieve_order_response:
type: object
properties:
partner_order_id:
type: string
example: '110001023'
description: The unique ID that is provided to Angi by the partner on order creation.
user_id:
type: string
example: xzvdfhryhbdbe
description: The unique ID that Angi has assigned to the consumer object that was created as a result of this order.
address_id:
type: string
example: sfgethrethrt
description: The unique ID that Angi has assigned the consumer's address object that was created as a result of this order.
products:
$ref: '#/definitions/recent_orders_product_responses'
created_at:
type: string
example: '2018-05-02T22:21:49.000Z'
processing_status:
type: string
example: complete
description: "If a partner is unsure about what is going on with an order, this field can provide more detail. The following statuses can be returned in this field: \n\n`failed`: order creation has failed. \n\n`partial`: some order products within this order have been created but there are others that are still processing. To see the statuses of individual products within this order, find the product in the `products` array and look for its `product_status`. \n\n`complete`: order creation is complete. \n\n`in_progress`: order creation is in progress."
partner_machine_name:
type: string
example: partner_name
description: The unique partner name given to the partner (same one that is used in the HTTP headers).
metadata:
type: object
x-nullable: true
description: Metadata that was passed in by the partner.
update_order_req_body:
type: object
description: Information about the order to update.
properties:
partner_order_id:
description: This is optional. Be sure to put the `partner_order_id` in the path parameter, that one is required.
type: string
example: '110001023'
products:
type: array
items:
type: object
properties:
sku:
$ref: '#/definitions/sku'
order_details:
$ref: '#/definitions/order_details_update'
scheduling_details:
$ref: '#/definitions/scheduling_details'
product_info:
type: object
description: The information passed in this object will be used to create or
# --- truncated at 32 KB (46 KB total) ---
# Full source: https://raw.githubusercontent.com/api-evangelist/handy/refs/heads/main/openapi/handy-orders-api-openapi.yml