{"openapi":"3.1.0","info":{"title":"Rovela Store Data API","version":"1.0.0","description":"Read-only REST API for exporting a Rovela store's data: orders, customers, products, and categories. Create an API key in the store dashboard under Settings then API. Full docs: https://rovela.ai/developers"},"servers":[{"url":"https://rovela.ai"}],"security":[{"apiKey":[]}],"tags":[{"name":"Store","description":"A snapshot of the store the key belongs to: identity, currency, configuration flags, and row counts per resource. Use it as a connection sanity check and to learn the currency your money fields are denominated in."},{"name":"Orders","description":"Orders placed on the storefront, with their line items embedded. Money fields are strings in the store currency. Guest orders have a null customer_id but always carry the buyer's email."},{"name":"Customers","description":"Customers who created an account or checked out on the storefront. Passwords are never exposed through the API, on any endpoint."},{"name":"Products","description":"The store catalog, with variants embedded on every product. Products without variants return an empty variants array; their price and inventory live on the product itself."},{"name":"Categories","description":"The store's category tree. Categories can nest one level via parent_id; a null parent_id marks a top-level category."}],"paths":{"/api/v1/store":{"get":{"operationId":"get-store","summary":"Retrieve the store","description":"Returns the store snapshot for the key you authenticate with. If this call works, every other endpoint will too.","tags":["Store"],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/Store"}}}}}},"401":{"description":"Missing, malformed, or revoked API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded. Honor the Retry-After header (seconds).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"default":{"description":"Error. See the error catalog in the docs for every code.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/orders":{"get":{"operationId":"list-orders","summary":"List orders","description":"Returns orders oldest first, line items embedded. Filter by status or by updated_since for incremental syncs.","tags":["Orders"],"parameters":[{"name":"limit","in":"query","required":false,"description":"Page size, 1 to 250. Defaults to 50.","schema":{"type":"integer"}},{"name":"cursor","in":"query","required":false,"description":"Opaque cursor from the previous page's meta.next_cursor.","schema":{"type":"string"}},{"name":"updated_since","in":"query","required":false,"description":"Only orders updated at or after this instant.","schema":{"type":"string","format":"date-time"}},{"name":"status","in":"query","required":false,"description":"One of pending, paid, shipped, delivered, cancelled, refunded, return_requested.","schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","required":["data","meta"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Order"}},"meta":{"type":"object","required":["next_cursor","has_more"],"properties":{"next_cursor":{"type":["string","null"],"description":"Opaque cursor for the next page, or null on the last page."},"has_more":{"type":"boolean"}}}}}}}},"401":{"description":"Missing, malformed, or revoked API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded. Honor the Retry-After header (seconds).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"default":{"description":"Error. See the error catalog in the docs for every code.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/orders/{id}":{"get":{"operationId":"get-order","summary":"Retrieve an order","description":"Returns a single order with its line items.","tags":["Orders"],"parameters":[{"name":"id","in":"path","required":true,"description":"The order id.","schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/Order"}}}}}},"401":{"description":"Missing, malformed, or revoked API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded. Honor the Retry-After header (seconds).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"default":{"description":"Error. See the error catalog in the docs for every code.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/customers":{"get":{"operationId":"list-customers","summary":"List customers","description":"Returns customers oldest first. Customer records are effectively append-only, so created_since gives you a clean incremental sync.","tags":["Customers"],"parameters":[{"name":"limit","in":"query","required":false,"description":"Page size, 1 to 250. Defaults to 50.","schema":{"type":"integer"}},{"name":"cursor","in":"query","required":false,"description":"Opaque cursor from the previous page's meta.next_cursor.","schema":{"type":"string"}},{"name":"created_since","in":"query","required":false,"description":"Only customers created at or after this instant.","schema":{"type":"string","format":"date-time"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","required":["data","meta"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Customer"}},"meta":{"type":"object","required":["next_cursor","has_more"],"properties":{"next_cursor":{"type":["string","null"],"description":"Opaque cursor for the next page, or null on the last page."},"has_more":{"type":"boolean"}}}}}}}},"401":{"description":"Missing, malformed, or revoked API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded. Honor the Retry-After header (seconds).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"default":{"description":"Error. See the error catalog in the docs for every code.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/customers/{id}":{"get":{"operationId":"get-customer","summary":"Retrieve a customer","description":"Returns a single customer.","tags":["Customers"],"parameters":[{"name":"id","in":"path","required":true,"description":"The customer id.","schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/Customer"}}}}}},"401":{"description":"Missing, malformed, or revoked API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded. Honor the Retry-After header (seconds).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"default":{"description":"Error. See the error catalog in the docs for every code.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/products":{"get":{"operationId":"list-products","summary":"List products","description":"Returns products oldest first, variants embedded. Filter by status to export only the live catalog, or by updated_since for incremental syncs.","tags":["Products"],"parameters":[{"name":"limit","in":"query","required":false,"description":"Page size, 1 to 250. Defaults to 50.","schema":{"type":"integer"}},{"name":"cursor","in":"query","required":false,"description":"Opaque cursor from the previous page's meta.next_cursor.","schema":{"type":"string"}},{"name":"updated_since","in":"query","required":false,"description":"Only products updated at or after this instant.","schema":{"type":"string","format":"date-time"}},{"name":"status","in":"query","required":false,"description":"One of draft, active, archived.","schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","required":["data","meta"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Product"}},"meta":{"type":"object","required":["next_cursor","has_more"],"properties":{"next_cursor":{"type":["string","null"],"description":"Opaque cursor for the next page, or null on the last page."},"has_more":{"type":"boolean"}}}}}}}},"401":{"description":"Missing, malformed, or revoked API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded. Honor the Retry-After header (seconds).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"default":{"description":"Error. See the error catalog in the docs for every code.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/products/{id}":{"get":{"operationId":"get-product","summary":"Retrieve a product","description":"Returns a single product with its variants.","tags":["Products"],"parameters":[{"name":"id","in":"path","required":true,"description":"The product id.","schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/Product"}}}}}},"401":{"description":"Missing, malformed, or revoked API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded. Honor the Retry-After header (seconds).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"default":{"description":"Error. See the error catalog in the docs for every code.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/categories":{"get":{"operationId":"list-categories","summary":"List categories","description":"Returns categories oldest first.","tags":["Categories"],"parameters":[{"name":"limit","in":"query","required":false,"description":"Page size, 1 to 250. Defaults to 50.","schema":{"type":"integer"}},{"name":"cursor","in":"query","required":false,"description":"Opaque cursor from the previous page's meta.next_cursor.","schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","required":["data","meta"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Category"}},"meta":{"type":"object","required":["next_cursor","has_more"],"properties":{"next_cursor":{"type":["string","null"],"description":"Opaque cursor for the next page, or null on the last page."},"has_more":{"type":"boolean"}}}}}}}},"401":{"description":"Missing, malformed, or revoked API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded. Honor the Retry-After header (seconds).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"default":{"description":"Error. See the error catalog in the docs for every code.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}}},"components":{"securitySchemes":{"apiKey":{"type":"apiKey","in":"header","name":"x-api-key","description":"Per-store API key (rvk_…), created in the store dashboard under Settings then API."}},"schemas":{"Error":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["INVALID_API_KEY","STORE_NOT_SUPPORTED","STORE_NOT_READY","INVALID_PARAM","INVALID_CURSOR","NOT_FOUND","RATE_LIMITED","STORE_DB_ERROR"]},"message":{"type":"string"}}}}},"Store":{"type":"object","description":"A snapshot of the store the key belongs to: identity, currency, configuration flags, and row counts per resource. Use it as a connection sanity check and to learn the currency your money fields are denominated in.","properties":{"id":{"type":"string","format":"uuid","description":"The store id on Rovela."},"name":{"type":"string","description":"The store name on the Rovela dashboard."},"store_name":{"type":["string","null"],"description":"The customer-facing store name."},"store_email":{"type":["string","null"],"description":"The store contact email."},"store_currency":{"type":["string","null"],"description":"ISO 4217 currency code all money fields use, for example \"USD\"."},"store_timezone":{"type":["string","null"],"description":"IANA timezone, for example \"America/New_York\"."},"tax_included_in_prices":{"type":["boolean","null"],"description":"Whether displayed prices already include tax."},"shipping_enabled":{"type":["boolean","null"],"description":"Whether the store ships physical goods."},"guest_checkout":{"type":["boolean","null"],"description":"Whether customers can buy without an account."},"customer_accounts":{"type":["boolean","null"],"description":"Whether customer accounts are enabled."},"counts":{"type":"object","description":"Row counts: products, categories, customers, orders."}}},"Order":{"type":"object","description":"Orders placed on the storefront, with their line items embedded. Money fields are strings in the store currency. Guest orders have a null customer_id but always carry the buyer's email.","properties":{"id":{"type":"string","format":"uuid","description":"Order id."},"customer_id":{"type":["string","null"],"format":"uuid","description":"The buyer's customer id, or null for guest checkout."},"email":{"type":"string","description":"The buyer's email. Always present."},"status":{"type":"string","description":"One of pending, paid, shipped, delivered, cancelled, refunded, return_requested."},"subtotal":{"type":"string","description":"Item total before tax, shipping, and discounts."},"tax":{"type":"string","description":"Tax charged."},"shipping":{"type":"string","description":"Shipping charged."},"total":{"type":"string","description":"Amount the customer paid."},"refund_amount":{"type":["string","null"],"description":"Total refunded so far."},"return_reason":{"type":["string","null"],"description":"The customer's reason when a return was requested."},"discount_code":{"type":["string","null"],"description":"The promotion code the customer redeemed, if any."},"discount_amount":{"type":["string","null"],"description":"Amount the discount removed."},"shipping_address":{"type":["object","null"],"description":"firstName, lastName, line1, line2, city, state, postalCode, country, phone."},"billing_address":{"type":["object","null"],"description":"Same shape as shipping_address."},"stripe_payment_intent_id":{"type":["string","null"],"description":"Stripe PaymentIntent id, for reconciliation."},"stripe_charge_id":{"type":["string","null"],"description":"Stripe Charge id."},"receipt_url":{"type":["string","null"],"description":"Stripe-hosted receipt for the customer."},"tracking_number":{"type":["string","null"],"description":"Shipment tracking number once fulfilled."},"tracking_url":{"type":["string","null"],"description":"Carrier tracking link."},"carrier":{"type":["string","null"],"description":"Carrier code, for example \"ups\"."},"shipping_method":{"type":["string","null"],"description":"The shipping option the customer chose."},"created_at":{"type":"string","format":"date-time","description":"When the order was placed."},"updated_at":{"type":"string","format":"date-time","description":"Last change to the order. Drives updated_since."},"items":{"type":"array","items":{"$ref":"#/components/schemas/OrderItem"},"description":"The order's line items, embedded. See Order item below."}}},"OrderItem":{"type":"object","description":"One line of an order. Name and price are snapshots taken at purchase time, so they stay correct even after the product changes.","properties":{"id":{"type":"string","format":"uuid","description":"Line item id."},"order_id":{"type":"string","format":"uuid","description":"The parent order."},"product_id":{"type":"string","format":"uuid","description":"The product purchased."},"variant_id":{"type":["string","null"],"format":"uuid","description":"The variant purchased, if the product has variants."},"name":{"type":"string","description":"Product name at purchase time."},"price":{"type":"string","description":"Unit price at purchase time."},"quantity":{"type":"integer","description":"Units purchased."},"attributes":{"type":["object","null"],"description":"Variant attributes at purchase time, for example {\"color\": \"Sand\"}."},"created_at":{"type":"string","format":"date-time","description":"When the line was written."}}},"Customer":{"type":"object","description":"Customers who created an account or checked out on the storefront. Passwords are never exposed through the API, on any endpoint.","properties":{"id":{"type":"string","format":"uuid","description":"Customer id."},"email":{"type":"string","description":"The customer's email. Unique within the store."},"name":{"type":["string","null"],"description":"The customer's name, when provided."},"stripe_customer_id":{"type":["string","null"],"description":"Stripe Customer id, for reconciliation."},"email_verified":{"type":["string","null"],"format":"date-time","description":"When the email was verified, or null if it never was."},"created_at":{"type":"string","format":"date-time","description":"When the customer record was created. Drives created_since."}}},"Product":{"type":"object","description":"The store catalog, with variants embedded on every product. Products without variants return an empty variants array; their price and inventory live on the product itself.","properties":{"id":{"type":"string","format":"uuid","description":"Product id."},"name":{"type":"string","description":"Product name."},"slug":{"type":"string","description":"URL slug, unique within the store."},"description":{"type":"string","description":"Product description."},"price":{"type":"string","description":"Base price. Variants can override it."},"compare_price":{"type":["string","null"],"description":"The crossed-out \"was\" price, when on sale."},"images":{"type":"array","items":{"type":"string"},"description":"Image URLs, in display order."},"videos":{"type":"array","items":{"type":"string"},"description":"Video URLs."},"status":{"type":"string","description":"One of draft, active, archived."},"category_id":{"type":["string","null"],"format":"uuid","description":"The category the product belongs to."},"has_variants":{"type":"boolean","description":"Whether the product sells in variants."},"sku":{"type":["string","null"],"description":"Stock keeping unit, when set."},"inventory":{"type":["integer","null"],"description":"Units in stock (for products without variants)."},"track_inventory":{"type":["boolean","null"],"description":"Whether stock is tracked."},"continue_selling_when_out_of_stock":{"type":["boolean","null"],"description":"Whether checkout stays open at zero stock."},"product_type":{"type":["string","null"],"description":"Free-form type label."},"vendor":{"type":["string","null"],"description":"Brand or supplier name."},"tags":{"type":["array","null"],"items":{"type":"string"},"description":"Free-form tags."},"meta_title":{"type":["string","null"],"description":"SEO title override."},"meta_description":{"type":["string","null"],"description":"SEO description override."},"meta_keywords":{"type":["array","null"],"items":{"type":"string"},"description":"SEO keywords."},"metafields":{"type":"array","items":{},"description":"Custom fields as {key, label, value} objects."},"created_at":{"type":"string","format":"date-time","description":"When the product was created."},"updated_at":{"type":"string","format":"date-time","description":"Last change. Drives updated_since."},"variants":{"type":"array","items":{"$ref":"#/components/schemas/ProductVariant"},"description":"The product's variants, embedded. See Variant below."}}},"ProductVariant":{"type":"object","description":"One sellable variation of a product, for example a size and color combination. A null price means the variant sells at the product's base price.","properties":{"id":{"type":"string","format":"uuid","description":"Variant id."},"product_id":{"type":"string","format":"uuid","description":"The parent product."},"sku":{"type":"string","description":"Stock keeping unit, unique within the store."},"name":{"type":"string","description":"Variant display name, for example \"Sand / Large\"."},"price":{"type":["string","null"],"description":"Price override, or null to use the product price."},"inventory":{"type":"integer","description":"Units in stock for this variant."},"image":{"type":["string","null"],"description":"Variant-specific image URL."},"video":{"type":["string","null"],"description":"Variant-specific video URL."},"attributes":{"type":"object","description":"The option values, for example {\"color\": \"Sand\", \"size\": \"Large\"}."},"created_at":{"type":"string","format":"date-time","description":"When the variant was created."}}},"Category":{"type":"object","description":"The store's category tree. Categories can nest one level via parent_id; a null parent_id marks a top-level category.","properties":{"id":{"type":"string","format":"uuid","description":"Category id."},"name":{"type":"string","description":"Category name."},"slug":{"type":"string","description":"URL slug, unique within the store."},"description":{"type":["string","null"],"description":"Category description."},"image":{"type":["string","null"],"description":"Cover image URL."},"parent_id":{"type":["string","null"],"format":"uuid","description":"The parent category, or null for top level."},"order":{"type":"integer","description":"Display position among siblings."},"created_at":{"type":"string","format":"date-time","description":"When the category was created."}}}}}}