POS Integration Guide (IMPORTANT)

This API is designed for fulfilling shop POS systems to receive and manage incoming GetBloomDirect orders.

Supported v1 Workflow

PENDING_ACCEPTANCE
              ├─ Accept  → ACCEPTED → Delivered → COMPLETED
              └─ Decline → DECLINED

What v1 POS integrations should support:

  • Retrieve incoming orders assigned to the fulfilling shop
  • Accept or decline pending orders
  • Require a decline reason when declining an order
  • Mark eligible orders as completed after fulfillment

Not included in v1

  • Creating outbound orders from the POS
  • Marking orders paid from the POS
  • Settlement calculations inside the POS

Money Format

GetBloomDirect stores monetary values internally in cents, but the POS API returns dollar amounts as JSON numbers in USD.

Example: 50 means $50.00, and 59.99 means $59.99.

Decline Reasons

  • OUT_OF_STOCK
  • TOO_BUSY
  • DELIVERY_TOO_FAR
  • OTHER

If declineReason = "OTHER", then declineMessage is required.

Example:

{
  "declineReason": "OTHER",
  "declineMessage": "We are closing early today"
}

Authentication

All external v1 API requests must include your shop's API key.

API key header

x-api-key: YOUR_API_KEY

Bearer authorization

Authorization: Bearer YOUR_API_KEY

Use one authentication method per request. The examples in this guide use the x-api-key header.

You can generate and manage your API key from your GetBloomDirect dashboard under POS API Access.

Only Pro shops can use the external POS API. If your subscription becomes inactive, API access will stop until Pro access is restored.

Orders API

GET/api/external/v1/orders

Retrieve incoming orders assigned to your fulfilling shop. Use the optional since parameter to return orders updated after a specific timestamp, and limit to control the number of results.

Headers

{
  "x-api-key": "YOUR_API_KEY"
}

Example Request

curl -X GET "https://www.getbloomdirect.com/api/external/v1/orders?since=2026-08-05T12:00:00.000Z&limit=100" \
  -H "x-api-key: YOUR_API_KEY"

Example Response

{
  "success": true,
  "data": {
    "orders": [
      {
        "id": "68935f106a9e3d72b872a101",
        "orderNumber": "1001",
        "status": "PENDING_ACCEPTANCE",
        "decline": {
          "reason": "",
          "message": ""
        },
        "recipient": {
          "fullName": "Jane Doe",
          "address": "123 Main St",
          "apt": "Apt 4B",
          "city": "Buffalo",
          "state": "NY",
          "zip": "14201",
          "phone": "555-123-4567",
          "email": "jane@example.com",
          "company": "Acme Corp",
          "message": "Happy Birthday!"
        },
        "customer": {
          "fullName": "John Smith",
          "email": "john@example.com",
          "phone": "555-987-6543"
        },
        "products": [
          {
            "name": "Red Roses",
            "description": "Dozen premium roses",
            "photo": "https://cdn.getbloomdirect.com/products/roses.jpg",
            "qty": 1,
            "taxable": true,
            "price": 59.99
          }
        ],
        "totals": {
          "currency": "USD",
          "productsSubtotal": 59.99,
          "deliveryFee": 10,
          "originatingFee": 0,
          "tax": 4.8,
          "orderTotal": 74.79,
          "fulfillmentAmount": 69.99
        },
        "delivery": {
          "date": "2026-08-08T00:00:00.000Z",
          "window": {
            "type": "specific",
            "from": "09:00",
            "to": "13:00"
          },
          "instructions": "Leave at front desk"
        },
        "paidAt": null,
        "timestamps": {
          "created": "2026-08-05T12:05:00.000Z",
          "accepted": null,
          "declined": null,
          "completed": null,
          "updated": "2026-08-05T12:05:00.000Z"
        }
      }
    ]
  },
  "meta": {
    "timestamp": "2026-08-06T13:30:00.000Z",
    "version": "1.0"
  }
}

Possible Errors

  • MISSING_API_KEY: No API key was supplied.
  • INVALID_API_KEY: The API key is invalid or API access is disabled.
  • PRO_REQUIRED: An active Bloom Pro subscription is required.
  • ACCOUNT_SUSPENDED: The shop account is suspended.
  • ACCOUNT_ARCHIVED: The shop account is no longer active.
  • ACCOUNT_RESTRICTED: The shop account is restricted from API access.
  • INVALID_REQUEST: The since or limit query parameter is invalid.
  • RATE_LIMIT_EXCEEDED: Too many requests were made.
  • SERVER_ERROR: The orders could not be retrieved.

Optional query parameters

  • since: ISO 8601 timestamp. Returns orders updated after this time.
  • limit: Positive whole number. Defaults to 100 and has a maximum of 250.
GET /api/external/v1/orders?since=2026-07-15T12:00:00.000Z&limit=100

Order response fields

The Orders endpoint and each order-action endpoint return the same normalized order object.

Delivery window

  • anytime: no specific delivery window was requested. The from and to values are empty strings.
  • specific: both values contain 24-hour HH:mm times.

Decline details

For non-declined orders, decline.reason and decline.message are empty strings. For declined orders, they contain the saved decline details.

Monetary totals

productsSubtotal
Total value of the order products.
deliveryFee
Delivery amount charged for the order.
originatingFee
Fee retained by the sending florist.
tax
Sales tax included in the customer total.
orderTotal
Total amount charged to the customer.
fulfillmentAmount
Product subtotal plus delivery fee owed to the fulfilling florist.

All monetary values are returned as USD numbers, not integer cents.

POST/api/external/v1/orders/:id/accept

Accept an incoming order.

Headers

{
  "x-api-key": "YOUR_API_KEY"
}

Example Request

curl -X POST "https://www.getbloomdirect.com/api/external/v1/orders/68935f106a9e3d72b872a101/accept" \
  -H "x-api-key: YOUR_API_KEY"

Example Response

{
  "success": true,
  "data": {
    "order": {
      "id": "68935f106a9e3d72b872a101",
      "orderNumber": "1001",
      "status": "ACCEPTED",
      "decline": {
        "reason": "",
        "message": ""
      },
      "recipient": {
        "fullName": "Jane Doe",
        "address": "123 Main St",
        "apt": "Apt 4B",
        "city": "Buffalo",
        "state": "NY",
        "zip": "14201",
        "phone": "555-123-4567",
        "email": "jane@example.com",
        "company": "Acme Corp",
        "message": "Happy Birthday!"
      },
      "customer": {
        "fullName": "John Smith",
        "email": "john@example.com",
        "phone": "555-987-6543"
      },
      "products": [
        {
          "name": "Red Roses",
          "description": "Dozen premium roses",
          "photo": "https://cdn.getbloomdirect.com/products/roses.jpg",
          "qty": 1,
          "taxable": true,
          "price": 59.99
        }
      ],
      "totals": {
        "currency": "USD",
        "productsSubtotal": 59.99,
        "deliveryFee": 10,
        "originatingFee": 0,
        "tax": 4.8,
        "orderTotal": 74.79,
        "fulfillmentAmount": 69.99
      },
      "delivery": {
        "date": "2026-08-08T00:00:00.000Z",
        "window": {
          "type": "specific",
          "from": "09:00",
          "to": "13:00"
        },
        "instructions": "Leave at front desk"
      },
      "paidAt": null,
      "timestamps": {
        "created": "2026-08-05T12:05:00.000Z",
        "accepted": "2026-08-05T14:12:00.000Z",
        "declined": null,
        "completed": null,
        "updated": "2026-08-05T14:12:00.000Z"
      }
    }
  },
  "meta": {
    "timestamp": "2026-08-05T14:12:00.000Z",
    "version": "1.0"
  }
}

Possible Errors

  • MISSING_API_KEY: No API key was supplied.
  • INVALID_API_KEY: The API key is invalid or API access is disabled.
  • PRO_REQUIRED: An active Bloom Pro subscription is required.
  • ACCOUNT_SUSPENDED: The shop account is suspended.
  • ACCOUNT_ARCHIVED: The shop account is no longer active.
  • ACCOUNT_RESTRICTED: The shop account is restricted from API access.
  • ORDER_NOT_FOUND: The order was not found.
  • FORBIDDEN: The order is assigned to a different fulfilling shop.
  • SHOP_NOT_READY_TO_ACCEPT: The shop is not currently eligible to accept orders.
  • INVALID_TRANSITION: The order cannot be accepted from its current status.
  • SERVER_ERROR: The order could not be accepted.
POST/api/external/v1/orders/:id/decline

Decline an incoming order. Requires declineReason. declineMessage is required only when declineReason is OTHER.

Headers

{
  "x-api-key": "YOUR_API_KEY",
  "Content-Type": "application/json"
}

Request Body

{
  "declineReason": "OTHER",
  "declineMessage": "We are closing early today"
}

Example Request

curl -X POST "https://www.getbloomdirect.com/api/external/v1/orders/68935f106a9e3d72b872a101/decline" \
  -H "x-api-key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "declineReason": "OTHER",
    "declineMessage": "We are closing early today"
  }'

Example Response

{
  "success": true,
  "data": {
    "order": {
      "id": "68935f106a9e3d72b872a101",
      "orderNumber": "1001",
      "status": "DECLINED",
      "decline": {
        "reason": "OTHER",
        "message": "We are closing early today"
      },
      "recipient": {
        "fullName": "Jane Doe",
        "address": "123 Main St",
        "apt": "Apt 4B",
        "city": "Buffalo",
        "state": "NY",
        "zip": "14201",
        "phone": "555-123-4567",
        "email": "jane@example.com",
        "company": "Acme Corp",
        "message": "Happy Birthday!"
      },
      "customer": {
        "fullName": "John Smith",
        "email": "john@example.com",
        "phone": "555-987-6543"
      },
      "products": [
        {
          "name": "Red Roses",
          "description": "Dozen premium roses",
          "photo": "https://cdn.getbloomdirect.com/products/roses.jpg",
          "qty": 1,
          "taxable": true,
          "price": 59.99
        }
      ],
      "totals": {
        "currency": "USD",
        "productsSubtotal": 59.99,
        "deliveryFee": 10,
        "originatingFee": 0,
        "tax": 4.8,
        "orderTotal": 74.79,
        "fulfillmentAmount": 69.99
      },
      "delivery": {
        "date": "2026-08-08T00:00:00.000Z",
        "window": {
          "type": "specific",
          "from": "09:00",
          "to": "13:00"
        },
        "instructions": "Leave at front desk"
      },
      "paidAt": null,
      "timestamps": {
        "created": "2026-08-05T12:05:00.000Z",
        "accepted": null,
        "declined": "2026-08-05T14:15:00.000Z",
        "completed": null,
        "updated": "2026-08-05T14:15:00.000Z"
      }
    }
  },
  "meta": {
    "timestamp": "2026-08-05T14:15:00.000Z",
    "version": "1.0"
  }
}

Possible Errors

  • MISSING_API_KEY: No API key was supplied.
  • INVALID_API_KEY: The API key is invalid or API access is disabled.
  • PRO_REQUIRED: An active Bloom Pro subscription is required.
  • ACCOUNT_SUSPENDED: The shop account is suspended.
  • ACCOUNT_ARCHIVED: The shop account is no longer active.
  • ACCOUNT_RESTRICTED: The shop account is restricted from API access.
  • ORDER_NOT_FOUND: The order was not found.
  • FORBIDDEN: The order is assigned to a different fulfilling shop.
  • MISSING_DECLINE_REASON: A decline reason is required.
  • INVALID_DECLINE_REASON: The supplied decline reason is not supported.
  • MISSING_DECLINE_MESSAGE: A message is required when the reason is "OTHER".
  • DECLINE_MESSAGE_TOO_LONG: The decline message is too long.
  • INVALID_TRANSITION: The order cannot be declined from its current status.
  • SERVER_ERROR: The order could not be declined.
POST/api/external/v1/orders/:id/complete

Mark an accepted order as completed after fulfillment. Orders in ACCEPTED status can be completed.

Headers

{
  "x-api-key": "YOUR_API_KEY"
}

Example Request

curl -X POST "https://www.getbloomdirect.com/api/external/v1/orders/68935f106a9e3d72b872a101/complete" \
  -H "x-api-key: YOUR_API_KEY"

Example Response

{
  "success": true,
  "data": {
    "order": {
      "id": "68935f106a9e3d72b872a101",
      "orderNumber": "1001",
      "status": "COMPLETED",
      "decline": {
        "reason": "",
        "message": ""
      },
      "recipient": {
        "fullName": "Jane Doe",
        "address": "123 Main St",
        "apt": "Apt 4B",
        "city": "Buffalo",
        "state": "NY",
        "zip": "14201",
        "phone": "555-123-4567",
        "email": "jane@example.com",
        "company": "Acme Corp",
        "message": "Happy Birthday!"
      },
      "customer": {
        "fullName": "John Smith",
        "email": "john@example.com",
        "phone": "555-987-6543"
      },
      "products": [
        {
          "name": "Red Roses",
          "description": "Dozen premium roses",
          "photo": "https://cdn.getbloomdirect.com/products/roses.jpg",
          "qty": 1,
          "taxable": true,
          "price": 59.99
        }
      ],
      "totals": {
        "currency": "USD",
        "productsSubtotal": 59.99,
        "deliveryFee": 10,
        "originatingFee": 0,
        "tax": 4.8,
        "orderTotal": 74.79,
        "fulfillmentAmount": 69.99
      },
      "delivery": {
        "date": "2026-08-08T00:00:00.000Z",
        "window": {
          "type": "specific",
          "from": "09:00",
          "to": "13:00"
        },
        "instructions": "Leave at front desk"
      },
      "paidAt": null,
      "timestamps": {
        "created": "2026-08-05T12:05:00.000Z",
        "accepted": "2026-08-05T14:12:00.000Z",
        "declined": null,
        "completed": "2026-08-08T15:00:00.000Z",
        "updated": "2026-08-08T15:00:00.000Z"
      }
    }
  },
  "meta": {
    "timestamp": "2026-08-08T15:00:00.000Z",
    "version": "1.0"
  }
}

Possible Errors

  • MISSING_API_KEY: No API key was supplied.
  • INVALID_API_KEY: The API key is invalid or API access is disabled.
  • PRO_REQUIRED: An active Bloom Pro subscription is required.
  • ACCOUNT_SUSPENDED: The shop account is suspended.
  • ACCOUNT_ARCHIVED: The shop account is no longer active.
  • ACCOUNT_RESTRICTED: The shop account is restricted from API access.
  • ORDER_NOT_FOUND: The order was not found.
  • FORBIDDEN: The order is assigned to a different fulfilling shop.
  • INVALID_TRANSITION: Only an accepted order can be marked delivered/completed.
  • SERVER_ERROR: The order could not be completed.

Error Reference

All API errors use the same response envelope. The HTTP status indicates the category of failure, while error.code provides a stable machine-readable value for your integration.

HTTPCodeMeaning
400INVALID_REQUESTThe request contains invalid input or malformed data.
400MISSING_DECLINE_REASONA decline request did not include a decline reason.
400MISSING_DECLINE_MESSAGEThe decline reason is "OTHER" but no explanatory message was supplied.
400INVALID_DECLINE_REASONThe supplied decline reason is not supported.
400DECLINE_MESSAGE_TOO_LONGThe decline message exceeds the allowed length.
401MISSING_API_KEYNo API key was supplied with the request.
401INVALID_API_KEYThe supplied API key is invalid, disabled, or no longer active.
403PRO_REQUIREDThe shop does not currently have an active Bloom Pro subscription.
403ACCOUNT_SUSPENDEDThe GetBloomDirect shop account is suspended.
403ACCOUNT_ARCHIVEDThe GetBloomDirect shop account is no longer active.
403ACCOUNT_RESTRICTEDThe shop account is restricted from API access.
403SHOP_NOT_READY_TO_ACCEPTThe fulfilling shop does not currently meet the requirements to accept orders.
403FORBIDDENThe authenticated shop is not permitted to perform the requested action.
404ORDER_NOT_FOUNDThe requested order does not exist or is not assigned to the authenticated fulfilling shop.
409INVALID_TRANSITIONThe request conflicts with the order's current lifecycle status.
429RATE_LIMIT_EXCEEDEDToo many API requests were made in a short period of time.
500SERVER_ERRORGetBloomDirect encountered an unexpected server-side error.

Error response format

{
  "success": false,
  "error": {
    "code": "INVALID_TRANSITION",
    "message": "Illegal order transition: DECLINED → ACCEPTED"
  },
  "meta": {
    "timestamp": "2026-08-07T12:00:00.000Z",
    "version": "1.0"
  }
}

Webhooks

GetBloomDirect can send order events to your POS webhook endpoint. Webhooks are intended for real-time updates, while the Orders API can be used for recovery polling.

Supported events

  • order.created — A new fulfillment order has been assigned to the shop.
  • order.accepted — The fulfilling shop accepted the order.
  • order.declined — The fulfilling shop declined the order.
  • order.completed — The fulfilling shop marked the order delivered and completed.

Request headers

  • X-Webhook-Signature: HMAC SHA-256 signature of the raw request body.
  • X-Webhook-Event: The event name.
  • X-Webhook-Delivery-Id: Unique identifier for this delivery.

Acknowledgment and duplicate protection

Return an HTTP 2xx response only after the event has been safely stored or processed.

Store each X-Webhook-Delivery-Id. If the same delivery ID is received again, do not process it twice. Return another 2xx response so GetBloomDirect stops retrying it.

Signature verification

Calculate an HMAC SHA-256 digest using your webhook secret and the raw request body. Compare the hexadecimal digest with X-Webhook-Signature.

const expectedSignature = crypto
                .createHmac("sha256", WEBHOOK_SECRET)
                .update(rawRequestBody)
                .digest("hex");

Retry policy

Failed deliveries are retried after approximately 1 minute, 5 minutes, 15 minutes, 1 hour, and 6 hours, for a maximum of six total delivery attempts including the initial attempt.

Missed webhook recovery

Webhooks are a real-time notification mechanism, not the authoritative source of order state. If your integration misses a webhook or is offline, recover changes by polling the Orders endpoint with the since query parameter.

GET /api/external/v1/orders?since=2026-08-05T12:00:00.000Z

Always treat the latest order returned by the Orders API as the current GetBloomDirect state.

Delivery order

Do not assume webhook deliveries will always arrive in lifecycle order. Network delays and retries can cause an older delivery to arrive after a newer one. Use the order's timestamps.updated value and periodically reconcile with the Orders API.

{
                "event": "order.completed",
                "data": {
                  "order": {
                    "id": "68935f106a9e3d72b872a101",
                    "orderNumber": "1001",
                    "status": "COMPLETED",
                    "decline": {
                      "reason": "",
                      "message": ""
                    },
                    "recipient": {
                      "fullName": "Jane Doe",
                      "address": "123 Main St",
                      "apt": "Apt 4B",
                      "city": "Buffalo",
                      "state": "NY",
                      "zip": "14201",
                      "phone": "555-123-4567",
                      "email": "jane@example.com",
                      "company": "Acme Corp",
                      "message": "Happy Birthday!"
                    },
                    "customer": {
                      "fullName": "John Smith",
                      "email": "john@example.com",
                      "phone": "555-987-6543"
                    },
                    "products": [
                      {
                        "name": "Red Roses",
                        "description": "Dozen premium roses",
                        "photo": "https://cdn.getbloomdirect.com/products/roses.jpg",
                        "qty": 1,
                        "taxable": true,
                        "price": 59.99
                      }
                    ],
                    "totals": {
                      "currency": "USD",
                      "productsSubtotal": 59.99,
                      "deliveryFee": 10,
                      "originatingFee": 0,
                      "tax": 4.8,
                      "orderTotal": 74.79,
                      "fulfillmentAmount": 69.99
                    },
                    "delivery": {
                      "date": "2026-08-08T00:00:00.000Z",
                      "window": {
                        "type": "specific",
                        "from": "09:00",
                        "to": "13:00"
                      },
                      "instructions": "Leave at front desk"
                    },
                    "paidAt": null,
                    "timestamps": {
                      "created": "2026-08-05T12:05:00.000Z",
                      "accepted": "2026-08-05T14:12:00.000Z",
                      "declined": null,
                      "completed": "2026-08-08T15:00:00.000Z",
                      "updated": "2026-08-08T15:00:00.000Z"
                    }
                  },
                  "actorShopId": "68935ee86a9e3d72b8729f11"
                },
                "meta": {
                  "timestamp": "2026-08-08T15:00:00.000Z",
                  "version": "1.0",
                  "deliveryId": "550e8400-e29b-41d4-a716-446655440000"
                }
              }