{
  "name": "Vanlo Shipping API Documentation",
  "description": "Machine-readable export of the Vanlo Shipping API documentation. Section content is Markdown; code examples are provided in shell, ruby, python, php and csharp.",
  "base_url": "https://www.vanlo.com/api/v1",
  "docs_url": "https://api-docs.vanlo.com",
  "sections": [
    {
      "id": "introduction",
      "title": "Introduction",
      "content": "# Introduction\n\nWelcome to the Vanlo API! You can use our API to verify addresses, rate and create shipments and shipping labels, and monitor tracking codes.\n\nThis is an EasyPost-compatible API, so switching from EasyPost to Vanlo is as simple as changing your request URL and API key!\n\nWe have client libraries / SDKs for Ruby, Python, PHP and C#. You can view code examples on the right, and you can switch the programming language of the examples with the tabs in the top right.\n\n### Request URL\nTest: `https://test.vanlo.com/api/v1/...`\n\nProduction: `https://www.vanlo.com/api/v1/...`"
    },
    {
      "id": "authentication",
      "title": "Authentication",
      "content": "# Authentication\n\n> Authentication is performed by including your API key with every request (cURL), or by setting it globally in one of our client libraries / SDKs:\n\n```shell\ncurl -X METHOD https://www.vanlo.com/api/v1/... \\\n  -H 'Authorization: Bearer VANLO_API_KEY'\n```\n\n```ruby\nrequire 'vanlo'\n\nVanlo.api_key = 'VANLO_API_KEY'\n```\n\n```python\nimport vanlo\n\nvanlo.api_key = 'VANLO_API_KEY'\n```\n\n```php\nrequire_once(\"/path/to/lib/vanlo.php\");\n\n\\Vanlo\\Vanlo::setApiKey('VANLO_API_KEY');\n```\n\n```csharp\nusing Vanlo;\n\nClientManager.SetCurrent(\"VANLO_API_KEY\");\n```\n\n> Make sure to replace `VANLO_API_KEY` with your API key.\n\nVanlo uses API keys for authentication and identification.\n\nVanlo expects for the API key to be included in all API requests as an authorization bearer token:\n\n`Authorization: Bearer VANLO_API_KEY`\n\n<aside class=\"notice\">\nYou must replace <code>VANLO_API_KEY</code> with your API key.\n</aside>"
    },
    {
      "id": "beta",
      "title": "Test Environment",
      "content": "# Test Environment\n\n> In the client libraries / SDKs you can access the test environment by setting the API base value before performing any actions:\n\n```shell\ncurl -X METHOD https://test.vanlo.com/api/v1/... \\\n  -H 'Authorization: Bearer VANLO_API_KEY'\n```\n\n```ruby\nrequire 'vanlo'\n\nVanlo.api_key = 'VANLO_API_KEY'\nVanlo.api_base = 'https://test.vanlo.com/api/v1'\n```\n\n```python\nimport vanlo\n\nvanlo.api_key = 'VANLO_API_KEY'\nvanlo.api_base = 'https://test.vanlo.com/api/v1'\n```\n\n```php\nrequire_once(\"/path/to/lib/vanlo.php\");\n\n\\Vanlo\\Vanlo::setApiKey('VANLO_API_KEY');\n\\Vanlo\\Vanlo::setApiBase('https://test.vanlo.com/api/v1');\n```\n\n```csharp\nusing Vanlo;\n\nClientManager.SetCurrent(\"VANLO_API_KEY\", \"https://test.vanlo.com/api/v1\");\n```\n\nVanlo offers a test environment which you can develop against without incurring any real charges or generating any live postage labels or tracking codes.\n\nYou can find your test API Key by logging in to the Vanlo Dashboard, entering \"test mode\" by clicking the toggle in the bottom of the left side bar, and then visiting the API Keys page: [https://dashboard.vanlo.com/apikey](https://dashboard.vanlo.com/apikey)"
    },
    {
      "id": "users",
      "title": "Users",
      "content": "# Users\n\n## User Object\n\nParameter | Type | Specification\n--------- | ---- | -------------\nid | string | Unique identifier, begins with \"user_\"\nemail | string |\nconfirmed | boolean | Whether the account has confirmed its email address\nconfirmed_at | datetime | When the account was confirmed, or `null` while pending\n\n## Confirm an Account\n\n<aside class=\"notice\">\nThis endpoint does not require an API key. The <code>confirmation_token</code> itself is the credential - it is single-use and tied to one account.\n</aside>\n\n```shell\ncurl -X POST https://www.vanlo.com/api/v1/users/confirmation \\\n  -d 'confirmation_token=abc123...'\n```\n\n```ruby\nrequire 'vanlo'\n\nVanlo::User.confirm(confirmation_token: 'abc123...')\n```\n\n```python\nimport vanlo\n\nvanlo.User.confirm(confirmation_token='abc123...')\n```\n\n```php\nrequire_once('/path/to/lib/vanlo.php');\n\n\\Vanlo\\User::confirm(array('confirmation_token' => 'abc123...'));\n```\n\n```csharp\nusing Vanlo;\n\nvar confirmParams = new Dictionary<string, object>() {\n    { \"confirmation_token\", \"abc123...\" }\n};\n\nUser.Confirm(confirmParams);\n```\n\n> The above command returns JSON structured like this:\n\n```json\n{\n  \"confirmed\": true,\n  \"email\": \"merchant@example.com\",\n  \"token\": \"eyJhbGciOi...\"\n}\n```\n\nConfirms an account by the token from the link in the confirmation email, and marks the account confirmed. On the confirming request, the response includes a short-lived opaque `token`, the same handoff value Vanlo's own web apps receive after registration - it is meant for a Vanlo front end to complete sign-in, not a value your integration decodes or stores.\n\nCalling this again with the same token after the account is already confirmed still returns `200` with `confirmed: true`, but the response has no `token` field - the link is single-use for signing in, even though confirming is safe to retry.\n\nAn unknown or malformed token returns `422` with `error.errors[0].field` set to `confirmation_token`. An expired token also returns `422`, but with the field set to `email` instead - the confirmation window is a property of the account, not of the token string.\n\n### HTTP Request\n\n`POST https://www.vanlo.com/api/v1/users/confirmation`\n\n### Confirm Request Parameters\n\nParameter | Type | Specification\n--------- | ---- | -------------\nconfirmation_token | string | Required. The token from the confirmation link.\n\n### Confirm Errors\n\nBeyond the [common errors](/#errors) shared by every endpoint, confirming can return:\n\nStatus | Code | When it happens | How to handle\n------ | ---- | ---------------- | -------------\n422 | `UNPROCESSABLE_ENTITY` | The token is unknown, malformed, or the confirmation window has expired. | Ask the account to request a new link via [Resend a Confirmation Email](#resend-a-confirmation-email).\n\n## Resend a Confirmation Email\n\n<aside class=\"notice\">\nThis endpoint does not require an API key.\n</aside>\n\n```shell\ncurl -X POST https://www.vanlo.com/api/v1/users/confirmation/resend \\\n  -d 'email=merchant@example.com'\n```\n\n```ruby\nrequire 'vanlo'\n\nVanlo::User.resend_confirmation(email: 'merchant@example.com')\n```\n\n```python\nimport vanlo\n\nvanlo.User.resend_confirmation(email='merchant@example.com')\n```\n\n```php\nrequire_once('/path/to/lib/vanlo.php');\n\n\\Vanlo\\User::resendConfirmation(array('email' => 'merchant@example.com'));\n```\n\n```csharp\nusing Vanlo;\n\nvar resendParams = new Dictionary<string, object>() {\n    { \"email\", \"merchant@example.com\" }\n};\n\nUser.ResendConfirmation(resendParams);\n```\n\n> The above command returns JSON structured like this:\n\n```json\n{\n  \"message\": \"If that address is registered and unconfirmed, we've sent a new link.\"\n}\n```\n\nRequests a new confirmation email. The response is identical `202` whether or not `email` belongs to a registered account, and whether or not that account is already confirmed - this endpoint never reveals which addresses are registered. A given address can be resent to at most once per minute; a request within that window returns `429` and the earlier link is still valid.\n\n### HTTP Request\n\n`POST https://www.vanlo.com/api/v1/users/confirmation/resend`\n\n### Resend Request Parameters\n\nParameter | Type | Specification\n--------- | ---- | -------------\nemail | string | Required. The address to send a new confirmation link to.\n\n### Resend Errors\n\nBeyond the [common errors](/#errors) shared by every endpoint, resending can return:\n\nStatus | Code | When it happens | How to handle\n------ | ---- | ---------------- | -------------\n422 | `UNPROCESSABLE_ENTITY` | `email` is missing or blank. | Send the address to resend the link to; `error.errors[0].field` is `email`.\n429 | `TOO_MANY_REQUESTS` | The same address was resent to within the last minute. | Wait for the `retry_after` seconds in `error.details`, then retry.\n\n## Retrieve the Current User\n\n```shell\ncurl -X GET https://www.vanlo.com/api/v1/users/me \\\n  -H 'Authorization: Bearer VANLO_API_KEY'\n```\n\n```ruby\nrequire 'vanlo'\nVanlo.api_key = 'VANLO_API_KEY'\n\nVanlo::User.me\n```\n\n```python\nimport vanlo\nvanlo.api_key = 'VANLO_API_KEY'\n\nvanlo.User.me()\n```\n\n```php\nrequire_once('/path/to/lib/vanlo.php');\n\\Vanlo\\Vanlo::setApiKey('VANLO_API_KEY');\n\n\\Vanlo\\User::me();\n```\n\n```csharp\nusing Vanlo;\nClientManager.SetCurrent(\"VANLO_API_KEY\");\n\nUser user = User.Me();\n```\n\n> The above command returns JSON structured like this:\n\n```json\n{\n  \"id\": \"user_...\",\n  \"email\": \"merchant@example.com\",\n  \"confirmed\": false,\n  \"confirmed_at\": null\n}\n```\n\nReturns the account behind the API key making the request, and whether it has confirmed its email. `confirmed_at` is `null` while confirmation is pending. This endpoint works whether or not the account is confirmed - it is how a client knows to show a \"confirm your email\" prompt in the first place.\n\n### HTTP Request\n\n`GET https://www.vanlo.com/api/v1/users/me`"
    },
    {
      "id": "shipments",
      "title": "Shipments",
      "content": "# Shipments\n\n<aside class=\"endpoint-guide-link\">\n  <svg width=\"20\" height=\"20\" viewBox=\"0 0 16.5 15.5\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\"><path d=\"M0.75 1.75C0.75 1.19772 1.19772 0.75 1.75 0.75H5.25035C6.04606 0.75 6.80918 1.0819 7.37184 1.67269C7.93449 2.26347 8.25059 3.06475 8.25059 3.90025V14.75C8.25059 14.1234 8.01352 13.6985 7.59153 13.2554C7.16954 12.8124 6.59719 12.5634 6.00041 12.5634H1.75C1.19771 12.5634 0.75 12.1157 0.75 11.5634V1.75Z\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/><path d=\"M15.7506 1.75C15.7506 1.19772 15.3029 0.75 14.7506 0.75H11.2502C10.4545 0.75 9.6914 1.0819 9.12875 1.67269C8.5661 2.26347 8.25 3.06475 8.25 3.90025V14.75C8.25 14.1234 8.48707 13.6985 8.90906 13.2554C9.33105 12.8124 9.90339 12.5634 10.5002 12.5634H14.7506C15.3029 12.5634 15.7506 12.1157 15.7506 11.5634V1.75Z\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/></svg>\n  <span>New to the Vanlo API? Walk through the <a href=\"#\" class=\"api-guides-btn\" data-guide=\"getting-started\">Getting Started Guide</a> for an end-to-end example of shipping your first package.</span>\n</aside>\n\n## Shipment Object\n\nParameter | Type | Specification\n--------- | ----- | -----\nid | string | Unique identifier, begins with \"shp_\"\nobject | string | \"Shipment\"\nusps_zone | integer |\tThe USPS zone of the shipment, if purchased with USPS\nto_address | [\\<Address\\>](/#addresses) | The destination address\nfrom_address | [\\<Address\\>](/#addresses) | The origin address\nparcel | [\\<Parcel\\>](/#parcels) | The dimensions and weight of the package\ncustoms_info | [\\<CustomsInfo\\>](/#customsinfos) | Information for the processing of customs\nrates | [[\\<Rate\\>](/#rate-object)...] | All associated Rate objects\nselected_rate | [\\<Rate\\>](/#rate-object) | The specific rate purchased for the shipment, or null if unpurchased or purchased through another mechanism\ntracker | [\\<Tracker\\>](/#trackers) | The associated Tracker object\npostage_label | [\\<PostageLabel\\>](/#postagelabel-object) | The associated PostageLabel object\nrefund_status | string | The current status of the shipment refund process. Possible values are \"submitted\", \"refunded\", \"rejected\"\ntracking_code | string | If purchased, the tracking code will appear here as well as within the Tracker object\nstatus | string | The current tracking status of the shipment\nis_return | boolean | Set `true` to create as a return\noptions | [\\<Options\\>](/#options-object) | All of the options passed to the shipment, discussed in more depth below\ninsurance | float | The declared insurance amount, if EasyPost insurance was purchased via the insurance_amount option\nreturn_address | [\\<Address\\>](/#addresses) | Optional return address, if different from the origin address\nbuyer_address | [\\<Address\\>](/#addresses) | Optional buyer address, if different from the destination address\nusps_tracking_code | string | USPS tracking code, present when purchased with a USPS service\ncreated_at | datetime | When the shipment was created\nupdated_at | datetime | When the shipment was last updated\n\n### Rate Object\n\nParameter | Type | Specification\n--------- | ----- | -----\nid | string | Unique identifier, begins with 'rate_'\nobject | string | \"Rate\"\ncarrier | string | name of carrier\nservice | string | name of service\nrate | string | the actual rate quote for this service\nrate_detail | object | The rate broken into its parts - `total_freight`, `total_surcharges`, and a `services` map naming each amount. See the fee blocks below\ndelivery_date | string | date for delivery\ndelivery_date_guaranteed | boolean | indicates if delivery window is guaranteed (true) or not (false)\ndelivery_days | string | delivery days for this service\ncreated_at | datetime |\n\nThe blocks below list the service values each carrier can return on rates, with the name and what each one is for. Which services you are quoted depends on your account.\n\n<details class=\"carrier-options\" open>\n<summary>USPS Services</summary>\n<table>\n<thead><tr><th>Service</th><th>Name</th><th>What it is for</th></tr></thead>\n<tbody>\n<tr><td>GroundAdvantage</td><td>Ground Advantage</td><td>Ground delivery for packages, the standard domestic choice.</td></tr>\n<tr><td>First</td><td>First</td><td>Lightweight domestic mail, priced for small parcels.</td></tr>\n<tr><td>Priority</td><td>Priority</td><td>Faster domestic delivery for packages of any weight class.</td></tr>\n<tr><td>Express</td><td>Express</td><td>The fastest domestic option, overnight to most locations.</td></tr>\n<tr><td>ParcelSelect</td><td>Parcel Select</td><td>Economy ground delivery for larger or high-volume shipments.</td></tr>\n<tr><td>MediaMail</td><td>Media Mail</td><td>Discounted rate restricted to books and media contents.</td></tr>\n<tr><td>LibraryMail</td><td>Library Mail</td><td>Discounted rate restricted to library materials.</td></tr>\n<tr><td>FirstClassMailInternational</td><td>First Class Mail International</td><td>Lightweight international mail.</td></tr>\n<tr><td>FirstClassPackageInternationalService</td><td>First Class Package International Service</td><td>Economy international delivery for small packages.</td></tr>\n<tr><td>PriorityMailInternational</td><td>Priority Mail International</td><td>Faster international delivery for packages.</td></tr>\n<tr><td>ExpressMailInternational</td><td>Express Mail International</td><td>The fastest international option.</td></tr>\n</tbody>\n</table>\n</details>\n\n<details class=\"carrier-options\">\n<summary>FedEx Services</summary>\n<table>\n<thead><tr><th>Service</th><th>Name</th><th>What it is for</th></tr></thead>\n<tbody>\n<tr><td>FEDEX_GROUND</td><td>Ground</td><td>Ground delivery to businesses.</td></tr>\n<tr><td>GROUND_HOME_DELIVERY</td><td>Home Delivery</td><td>Ground delivery to residential addresses.</td></tr>\n<tr><td>FEDEX_2_DAY</td><td>FedEx 2Day</td><td>Delivery in two business days.</td></tr>\n<tr><td>FEDEX_2_DAY_AM</td><td>FedEx 2Day AM</td><td>Delivery in two business days by the morning.</td></tr>\n<tr><td>FEDEX_EXPRESS_SAVER</td><td>ExpressSaver</td><td>Express delivery in three business days.</td></tr>\n<tr><td>STANDARD_OVERNIGHT</td><td>FedEx Standard Overnight</td><td>Next business day by the end of day.</td></tr>\n<tr><td>PRIORITY_OVERNIGHT</td><td>FedEx Priority Overnight</td><td>Next business day by the morning.</td></tr>\n<tr><td>FIRST_OVERNIGHT</td><td>FedEx First Overnight</td><td>Next business day, earliest available delivery.</td></tr>\n<tr><td>INTERNATIONAL_ECONOMY</td><td>FedEx Intl Economy</td><td>Economy international delivery.</td></tr>\n<tr><td>INTERNATIONAL_GROUND</td><td>International Ground</td><td>Ground delivery to Canada.</td></tr>\n<tr><td>INTERNATIONAL_FIRST</td><td>InternationalFirst</td><td>The earliest international express delivery.</td></tr>\n<tr><td>INTERNATIONAL_PRIORITY</td><td>FedEx Intl Priority</td><td>Fast international delivery.</td></tr>\n<tr><td>SMART_POST</td><td>SmartPost</td><td>Economy delivery with final handoff to USPS.</td></tr>\n</tbody>\n</table>\n</details>\n\n<details class=\"carrier-options\">\n<summary>UPS Services</summary>\n<table>\n<thead><tr><th>Service</th><th>Name</th><th>What it is for</th></tr></thead>\n<tbody>\n<tr><td>Ground</td><td>Ground</td><td>Ground delivery, the standard domestic choice.</td></tr>\n<tr><td>3DaySelect</td><td>3 Day Select</td><td>Delivery in three business days.</td></tr>\n<tr><td>2ndDayAir</td><td>2nd Day Air</td><td>Delivery in two business days.</td></tr>\n<tr><td>2ndDayAirAM</td><td>2nd Day Air AM</td><td>Delivery in two business days by the morning.</td></tr>\n<tr><td>NextDayAirSaver</td><td>Next Day Air Saver</td><td>Next business day by the end of day.</td></tr>\n<tr><td>NextDayAir</td><td>Next Day Air</td><td>Next business day by the afternoon.</td></tr>\n<tr><td>NextDayAirEarlyAM</td><td>Next Day Air Early AM</td><td>Next business day, earliest available delivery.</td></tr>\n<tr><td>UPSStandard</td><td>UPS Standard</td><td>Ground delivery to Canada and Mexico.</td></tr>\n<tr><td>UPSSaver</td><td>UPS Saver</td><td>International express by the end of day.</td></tr>\n<tr><td>Express</td><td>Express</td><td>International express by the morning.</td></tr>\n<tr><td>ExpressPlus</td><td>Express Plus</td><td>International express, earliest available delivery.</td></tr>\n<tr><td>Expedited</td><td>Expedited</td><td>Economy international delivery.</td></tr>\n</tbody>\n</table>\n</details>\n\n<details class=\"carrier-options\">\n<summary>UniUni Services</summary>\n<table>\n<thead><tr><th>Service</th><th>Name</th><th>What it is for</th></tr></thead>\n<tbody>\n<tr><td>UniUni</td><td>UniUni</td><td>Last-mile delivery, the single UniUni service.</td></tr>\n</tbody>\n</table>\n</details>\n\n<details class=\"carrier-options\">\n<summary>OSM Services</summary>\n<table>\n<thead><tr><th>Service</th><th>Name</th><th>What it is for</th></tr></thead>\n<tbody>\n<tr><td>Parcel</td><td>Parcel</td><td>Economy parcel delivery, the cheapest OSM option.</td></tr>\n<tr><td>GroundAdvantage</td><td>Ground Advantage</td><td>Ground delivery with final handoff to USPS.</td></tr>\n<tr><td>Priority</td><td>Priority</td><td>Faster delivery with final handoff to USPS.</td></tr>\n<tr><td>GlobalStandard</td><td>Global Standard</td><td>Economy international delivery.</td></tr>\n<tr><td>GlobalPriority</td><td>Global Priority</td><td>Faster international delivery.</td></tr>\n<tr><td>GlobalEPacket</td><td>Global E Packet</td><td>International delivery for lightweight packets.</td></tr>\n<tr><td>GlobalParcel</td><td>Global Parcel</td><td>International delivery for parcels.</td></tr>\n</tbody>\n</table>\n</details>\n\n<details class=\"carrier-options\">\n<summary>OneParcel Services</summary>\n<table>\n<thead><tr><th>Service</th><th>Name</th><th>What it is for</th></tr></thead>\n<tbody>\n<tr><td>ECOCE</td><td>OneParcel Economy Central</td><td>Economy delivery in the Central region.</td></tr>\n<tr><td>ECOEA</td><td>OneParcel Economy East</td><td>Economy delivery in the East region.</td></tr>\n<tr><td>ECONE</td><td>OneParcel Economy Northeast</td><td>Economy delivery in the Northeast region.</td></tr>\n<tr><td>ECONJ</td><td>OneParcel Economy New Jersey</td><td>Economy delivery from New Jersey.</td></tr>\n<tr><td>ECOSE</td><td>OneParcel Economy Southeast</td><td>Economy delivery in the Southeast region.</td></tr>\n<tr><td>ECOSL</td><td>OneParcel Economy Salt Lake City</td><td>Economy delivery from Salt Lake City.</td></tr>\n<tr><td>ECOSO</td><td>OneParcel Economy South</td><td>Economy delivery in the South region.</td></tr>\n<tr><td>ECOWE</td><td>OneParcel Economy West</td><td>Economy delivery in the West region.</td></tr>\n<tr><td>STDCE</td><td>OneParcel Standard Central</td><td>Standard delivery in the Central region.</td></tr>\n<tr><td>STDEA</td><td>OneParcel Standard East</td><td>Standard delivery in the East region.</td></tr>\n<tr><td>STDNE</td><td>OneParcel Standard Northeast</td><td>Standard delivery in the Northeast region.</td></tr>\n<tr><td>STDNJ</td><td>OneParcel Standard New Jersey</td><td>Standard delivery from New Jersey.</td></tr>\n<tr><td>STDSE</td><td>OneParcel Standard Southeast</td><td>Standard delivery in the Southeast region.</td></tr>\n<tr><td>STDSL</td><td>OneParcel Standard Salt Lake City</td><td>Standard delivery from Salt Lake City.</td></tr>\n<tr><td>STDSO</td><td>OneParcel Standard South</td><td>Standard delivery in the South region.</td></tr>\n<tr><td>STDWE</td><td>OneParcel Standard West</td><td>Standard delivery in the West region.</td></tr>\n<tr><td>EXPNJ</td><td>OneParcel Expedited New Jersey</td><td>Expedited delivery from New Jersey.</td></tr>\n<tr><td>REGSL</td><td>OneParcel Regional Salt Lake</td><td>Regional delivery around Salt Lake City.</td></tr>\n<tr><td>BPMCE</td><td>Bound Printed Matter Central</td><td>Printed-matter rate in the Central region.</td></tr>\n<tr><td>BPMNE</td><td>Bound Printed Matter Northeast</td><td>Printed-matter rate in the Northeast region.</td></tr>\n<tr><td>BPMSO</td><td>Bound Printed Matter South</td><td>Printed-matter rate in the South region.</td></tr>\n<tr><td>BPMWE</td><td>Bound Printed Matter West</td><td>Printed-matter rate in the West region.</td></tr>\n</tbody>\n</table>\n<p>Some OneParcel rates come from partner networks, so codes outside this list can appear.</p>\n</details>\n\n<details class=\"carrier-options\">\n<summary>P2PG Services</summary>\n<table>\n<thead><tr><th>Service</th><th>Name</th><th>What it is for</th></tr></thead>\n<tbody>\n<tr><td>PARCEL_FLEX_STD</td><td>Parcel Flex Standard</td><td>Standard parcel delivery.</td></tr>\n<tr><td>PARCEL_FLEX_ADV</td><td>Parcel Flex Advantage</td><td>Faster parcel delivery.</td></tr>\n<tr><td>PARCEL_FLEX_ADV_PL</td><td>Parcel Flex Advantage Plus</td><td>Faster parcel delivery with wider coverage.</td></tr>\n<tr><td>PARCEL_FLEX_ECO</td><td>Parcel Flex Economy</td><td>Economy parcel delivery.</td></tr>\n<tr><td>EXPRESS_GND</td><td>Express Ground</td><td>Ground express delivery.</td></tr>\n<tr><td>EXPRESS_1D</td><td>Express 1 Day</td><td>Delivery in one business day.</td></tr>\n<tr><td>EXPRESS_2D</td><td>Express 2 Day</td><td>Delivery in two business days.</td></tr>\n<tr><td>EXPRESS_3D</td><td>Express 3 Day</td><td>Delivery in three business days.</td></tr>\n<tr><td>SPEEDX_REGIONAL</td><td>SpeedX Regional</td><td>Regional last-mile delivery.</td></tr>\n<tr><td>INTL_CA</td><td>International Canada</td><td>Delivery to Canada.</td></tr>\n<tr><td>INTL_STD</td><td>International Standard</td><td>Standard international delivery.</td></tr>\n</tbody>\n</table>\n<p>The services you are quoted depend on your P2PG contract.</p>\n</details>\n\n<details class=\"carrier-options\">\n<summary>DoorDash Services</summary>\n<table>\n<thead><tr><th>Service</th><th>Name</th><th>What it is for</th></tr></thead>\n<tbody>\n<tr><td>Drive</td><td>DoorDash Drive</td><td>Same-day local delivery by a driver, the single DoorDash service.</td></tr>\n</tbody>\n</table>\n</details>\n\n<details class=\"carrier-options\">\n<summary>OnTrac Services</summary>\n<table>\n<thead><tr><th>Service</th><th>Name</th><th>What it is for</th></tr></thead>\n<tbody>\n<tr><td>RD</td><td>Routine Delivery</td><td>Regional ground delivery, the standard OnTrac service.</td></tr>\n<tr><td>SD</td><td>Same Day</td><td>Same-day regional delivery.</td></tr>\n</tbody>\n</table>\n<p>OnTrac quotes whatever services your contract carries; these are the two standard ones.</p>\n</details>\n\nEvery rate also carries a `rate_detail` object that breaks the price into its parts. `total_freight` is the transport charge, `total_surcharges` is the extra fees added on top, and `services` names each amount: the first entry is the transport charge itself, and every entry after it is one extra fee. The three always add up - the freight plus the surcharges is the rate you pay. This is the same object the Shipment report writes into its `rate_details` column, so the blocks below read the same whether you are looking at an API response or a CSV export.\n\nMost carriers name their entries in words, so there is nothing to look up. USPS names them with its own codes, which is why its block carries a table.\n\n<details class=\"carrier-options\" open>\n<summary>USPS Fees</summary>\n<p>USPS entries are 15-character USPS codes. The first is the postage, and each one below is an extra fee. Every code here ends in <code>0000</code>.</p>\n<table>\n<thead><tr><th>Code</th><th>Fee</th><th>When it applies</th></tr></thead>\n<tbody>\n<tr><td>DXSP0EXXXXX0000<br>DXSV0EXXXXX0000<br>DXSL0EXXXXX0000<br>DXSM0EXXXXX0000</td><td>Signature confirmation</td><td>You asked for signature confirmation. The code differs by service: Priority Mail and Ground Advantage, then Parcel Select, Library Mail and Media Mail.</td></tr>\n<tr><td>DXSP0EJXXXX0000<br>DXSV0EJXXXX0000<br>DXSL0EJXXXX0000<br>DXSM0EJXXXX0000<br>DXSF0EJPXXX0000</td><td>Signature confirmation, restricted delivery</td><td>You asked for signature confirmation restricted to the named recipient.</td></tr>\n<tr><td>DXAX0XXXXXX0000</td><td>Adult signature</td><td>You asked for an adult signature.</td></tr>\n<tr><td>DXBX0XXXXXX0000</td><td>Adult signature, restricted delivery</td><td>You asked for an adult signature restricted to the named recipient.</td></tr>\n<tr><td>DX1F0XXLXRX0000</td><td>Non-machinable letter</td><td>A First-Class letter USPS cannot run through its letter equipment - the wrong length-to-width ratio, or too thin for its size.</td></tr>\n<tr><td>D811XUXXXXX0000<br>D811XXXXXXX0000</td><td>Long parcel</td><td>The longest side measures more than 22 inches.</td></tr>\n<tr><td>D812XUXXXXX0000<br>D812XXXXXXX0000</td><td>Long parcel, over 30 inches</td><td>The longest side measures more than 30 inches. The code differs by service: Ground Advantage, then every other service.</td></tr>\n<tr><td>D813XUXXXXX0000<br>D813XXXXXXX0000</td><td>Oversized parcel</td><td>The parcel measures more than 2 cubic feet. The code differs by service: Ground Advantage, then every other service.</td></tr>\n<tr><td>DX0P0HXXXCX0000<br>DX0E0HXXXCX0000<br>DX0U0HXXXCX0000<br>DX0V0HXXXCX0000</td><td>Hazardous materials</td><td>You declared hazardous contents. The code differs by service: Priority Mail, Priority Mail Express, Ground Advantage, then Parcel Select. It is charged on Priority Mail and Priority Mail Express, and free on Ground Advantage and Parcel Select.</td></tr>\n</tbody>\n</table>\n<p><strong>Priority Mail Express carries no separate signature fee.</strong> That service already includes a signature, so no signature code appears on it. An adult signature is still charged.</p>\n<p><strong>The two length fees never both appear.</strong> A parcel over 30 inches is charged the over-30 fee instead of the over-22 one, not on top of it. An oversized parcel is separate and can appear alongside either.</p>\n<p><strong>Media Mail and Library Mail carry no length or oversized fee.</strong></p>\n<p><strong>International shipments carry almost nothing.</strong> No signature, adult signature, length, oversized or non-machinable fee can appear on an international USPS shipment. Hazardous materials is the one exception.</p>\n<p>USPS publishes the current price for each of these fees in <a href=\"https://pe.usps.com/text/dmm300/Notice123.htm\">Notice 123, the USPS price list</a>.</p>\n</details>\n\n<!--\n<details class=\"carrier-options\">\n<summary>FedEx Fees</summary>\n<p>FedEx entries are named in words. The first is the service value, and each one after it is a FedEx surcharge type as FedEx returns it - <code>FUEL</code>, <code>RESIDENTIAL_DELIVERY</code>, <code>DELIVERY_AREA</code>, <code>SIGNATURE_OPTION</code>, <code>ADDITIONAL_HANDLING</code>, <code>OVERSIZE</code> and <code>INSURED_VALUE</code> among them.</p>\n<p>FedEx shipments also have a report of their own. The FedEx Detail report puts the fuel, delivery-area and remaining surcharges into columns, quoted against final, which is the better view when you are auditing a carrier invoice.</p>\n</details>\n-->\n\n<!--\n<details class=\"carrier-options\">\n<summary>UPS Fees</summary>\n<p>UPS entries are named in words. The first is the service value, and each one after it is a UPS surcharge type as UPS returns it.</p>\n</details>\n-->\n\n<!--\n<details class=\"carrier-options\">\n<summary>UniUni Fees</summary>\n<p>UniUni returns two entries: <code>FREIGHT</code> for the transport charge and <code>FUEL</code> for the fuel surcharge.</p>\n</details>\n-->\n\n<!--\n<details class=\"carrier-options\">\n<summary>OSM Fees</summary>\n<p>The first entry is the service value. The extra fees are <code>Fuel</code>, <code>DAS</code> for a delivery-area surcharge, and <code>Misc</code> for anything else on the quote.</p>\n</details>\n-->\n\n<!--\n<details class=\"carrier-options\">\n<summary>OneParcel Fees</summary>\n<p>The transport charge is the <code>shipping</code> entry, and each remaining entry is one named extra fee.</p>\n</details>\n-->\n\n<!--\n<details class=\"carrier-options\">\n<summary>P2PG Fees</summary>\n<p>The first entry is the service value, and each one after it is a P2PG surcharge under the name P2PG returns it.</p>\n</details>\n-->\n\n<!--\n<details class=\"carrier-options\">\n<summary>DoorDash Fees</summary>\n<p>DoorDash quotes one all-in price, so there is no breakdown to read. <code>rate_detail</code> is empty and the rate is the whole charge.</p>\n</details>\n-->\n\n<!--\n<details class=\"carrier-options\">\n<summary>OnTrac Fees</summary>\n<p>OnTrac quotes one all-in price, so there is no breakdown to read. <code>rate_detail</code> is empty and the rate is the whole charge.</p>\n</details>\n-->\n\n### PostageLabel Object\n\nParameter | Type | Specification\n--------- | ----- | -----\nid | string | Unique identifier, begins with 'pl_'\nobject | string | \"PostageLabel\"\nintegrated_form | string |\nlabel_date | string | Date on label\nlabel_epl2_url | string | URL for epl2 label(if applicable)\nlabel_pdf_url | string | URL for PDF label(if applicable)\nlabel_zpl_url | string | URL for ZPL label(if applicable)\nlabel_resolution | string | Resolution of label image\nlabel_size | string | size of label\nlabel_type | string | Type of label\nlabel_file_type | string | Label file type\nlabel_url | string | URL of label image\ncreated_at | datetime |\nupdated_at | datetime |\n\n### Options Object\n\nParameter | Type | Specification\n--------- | ----- | -----\ncarrier_insurance_amount | float | Set the declared value in USD to purchase carrier insurance (e.g. FedEx). Cannot be used together with insurance_amount.\ninsurance_amount | float | Set the declared value in USD to purchase EasyPost insurance. Currently available for USPS shipments only (requires EasyPost contract). Cannot be used together with carrier_insurance_amount.\ncreate_and_buy | boolean | Set this to true to create and buy the label in one pass with the service given in the same request.\ndelivery_confirmation | string | Request a signature. Look for valid values in the block below. NO_SIGNATURE is the default.\nhazmat | string | Set this if the package includes hazardous materials. Look for valid values in the USPS Options block below.\nis_return | boolean | Set this to true if you want to use the return service sub-type.\nlabel_format | string | Supported label formats are \"PNG\", \"PDF\", \"ZPL\", and \"EPL2\". \"PNG\" is the only format that allows for conversion.\nlabel_size | string | Physical size of the label to be printed in inches. \"4X6\" or \"8.5X11\". Default is \"4X6\" which is the standard size suitable for label writers. \"8.5X11\" provides the same physical size of the label and adds blank space around for lazer printers and such.\nlabel_date | string | Set the date that will appear on the postage label. Accepts ISO 8601 formatted string including time zone offset. Vanlo stores all dates as UTC time.\npostage_label_inline | boolean | Set this to true to receive a base64-encoded label image in the label_file field of the response instead of a label url. Combine with create_and_buy option to get the label image with a single request.\nprint_custom | [[\\<PrintCustom\\>](/#printcustom-object)...] | You can optionally print custom messages on labels. The locations of these fields show up on different spots on the carrier's labels. How many entries actually print depends on the carrier and service; for USPS, see the PrintCustom Object notes below.\n\n<details class=\"carrier-options\" open>\n<summary>USPS Options</summary>\n<table>\n<thead><tr><th>Parameter</th><th>Type</th><th>Specification</th></tr></thead>\n<tbody>\n<tr><td>fetch_express_delivery_date</td><td>boolean</td><td>Set to true to get the guaranteed delivery date for USPS Express service.</td></tr>\n<tr><td>machinable</td><td>boolean</td><td>Default is true. Set this to false to mark the package as non-machinable. Applicable for USPS First shipments.</td></tr>\n<tr><td>special_rates_eligibility</td><td>string</td><td>This option allows you to request restrictive rates from USPS. Can set to 'USPS.MEDIAMAIL' or 'USPS.LIBRARYMAIL'.</td></tr>\n</tbody>\n</table>\n<p><strong>Valid values for delivery_confirmation:</strong></p>\n<ul>\n<li>NO_SIGNATURE</li>\n<li>SIGNATURE</li>\n<li>ADULT_SIGNATURE</li>\n<li>SIGNATURE_RESTRICTED</li>\n<li>ADULT_SIGNATURE_RESTRICTED</li>\n</ul>\n<p><strong>USPS values for hazmat:</strong></p>\n<ul>\n<li>PRIMARY_CONTAINED</li>\n<li>PRIMARY_PACKED</li>\n<li>PRIMARY</li>\n<li>SECONDARY_CONTAINED</li>\n<li>SECONDARY_PACKED</li>\n<li>SECONDARY</li>\n<li>ORMD</li>\n<li>LIMITED_QUANTITY</li>\n<li>LITHIUM</li>\n<li>AIR_ELIGIBLE_ETHANOL</li>\n<li>CLASS_1</li>\n<li>CLASS_3</li>\n<li>CLASS_7</li>\n<li>CLASS_8_CORROSIVE</li>\n<li>CLASS_8_WET_BATTERY</li>\n<li>CLASS_9_NEW_LITHIUM_INDIVIDUAL</li>\n<li>CLASS_9_USED_LITHIUM</li>\n<li>CLASS_9_NEW_LITHIUM_DEVICE</li>\n<li>CLASS_9_DRY_ICE</li>\n<li>CLASS_9_UNMARKED_LITHIUM</li>\n<li>CLASS_9_MAGNETIZED</li>\n<li>DIVISION_4_1</li>\n<li>DIVISION_5_1</li>\n<li>DIVISION_5_2</li>\n<li>DIVISION_6_1</li>\n<li>DIVISION_6_2</li>\n<li>EXCEPTED_QUANTITY_PROVISION</li>\n<li>GROUND_ONLY</li>\n<li>ID8000</li>\n<li>LIGHTERS</li>\n<li>SMALL_QUANTITY_PROVISION</li>\n</ul>\n</details>\n\n<details class=\"carrier-options\">\n<summary>FedEx Options</summary>\n<table>\n<thead><tr><th>Parameter</th><th>Type</th><th>Specification</th></tr></thead>\n<tbody>\n<tr><td>endorsement</td><td>string</td><td>Ancillary endorsement request for Smartpost. Valid values: ADDRESS_CORRECTION, CARRIER_LEAVE_IF_NO_RESPONSE, CHANGE_SERVICE, FORWARDING_SERVICE, RETURN_SERVICE</td></tr>\n<tr><td>smartpost_manifest</td><td>string</td><td></td></tr>\n<tr><td>smartpost_hub</td><td>string</td><td>Smartpost hub ID</td></tr>\n<tr><td>saturday_delivery</td><td>boolean</td><td>Set this to true for FedEx shipments to request Saturday delivery</td></tr>\n</tbody>\n</table>\n</details>\n\n<details class=\"carrier-options\">\n<summary>UniUni Options</summary>\n<table>\n<thead><tr><th>Parameter</th><th>Type</th><th>Specification</th></tr></thead>\n<tbody>\n<tr><td>hub_id</td><td>integer</td><td>Pickup warehouse ID. Required for UniUni shipments. See <a href=\"https://docs.uniuni.com\">the hub sheet at docs.uniuni.com</a> for valid values.</td></tr>\n<tr><td>batch_number</td><td>string</td><td>Associate the shipment with a batch</td></tr>\n<tr><td>tracking_number</td><td>string</td><td>You can set your custom tracking code using this option. Make sure it's unique.</td></tr>\n<tr><td>bag_number</td><td>string</td><td>Print the bag number on the label</td></tr>\n<tr><td>buzz_code</td><td>string</td><td>Print the recipient buzz code on the label</td></tr>\n<tr><td>total_value</td><td>float</td><td>Set the value of the shipment contents for help with lost packages</td></tr>\n<tr><td>driver_notes</td><td>string</td><td>Print the notes for the driver on the label</td></tr>\n<tr><td>danger_type</td><td>integer</td><td>In UniUni an analog of hazmat is set as a sum of the numbers representing contents properties: 1: Fragile; 8: Has battery; 16: Sensitive; 32: Has Liquid. For example, if it has all the hazardous properties at the same time, pass 57.</td></tr>\n</tbody>\n</table>\n</details>\n\n<details class=\"carrier-options\">\n<summary>DoorDash Options</summary>\n<table>\n<thead><tr><th>Parameter</th><th>Type</th><th>Specification</th></tr></thead>\n<tbody>\n<tr><td>pickup_begin_time</td><td>string</td><td>When the package is ready for collection. Printed on the DoorDash pickup instructions.</td></tr>\n<tr><td>delivery_instructions</td><td>string</td><td>Instructions shown to the driver at the drop-off. Takes precedence over special_instructions.</td></tr>\n<tr><td>special_instructions</td><td>string</td><td>Used as the drop-off instructions when delivery_instructions is not set.</td></tr>\n<tr><td>total_value</td><td>float</td><td>Value of the shipment contents in USD. DoorDash prices partly on this, so set it on every shipment.</td></tr>\n</tbody>\n</table>\n<p><strong>Not supported by DoorDash:</strong> delivery_confirmation, hazmat and insurance.</p>\n</details>\n\n<details class=\"carrier-options\">\n<summary>OnTrac Options</summary>\n<table>\n<thead><tr><th>Parameter</th><th>Type</th><th>Specification</th></tr></thead>\n<tbody>\n<tr><td>pickup_type</td><td>string</td><td>How the package reaches the carrier. Valid values: None (you hand it over yourself, the default) and LaserShip (OnTrac collects it). The value keeps the LaserShip name used by the carrier's own API.</td></tr>\n<tr><td>pickup_begin_time</td><td>string</td><td>Earliest UTC time the package is ready for pickup, for example 2026-07-28T20:00:00.</td></tr>\n<tr><td>pickup_end_time</td><td>string</td><td>UTC time the package leaves the origin, for example 2026-07-28T22:00:00. Defaults to label_date, or to now.</td></tr>\n<tr><td>delivery_instructions</td><td>string</td><td>Instruction printed for the driver at the destination. Takes precedence over special_instructions.</td></tr>\n<tr><td>special_instructions</td><td>string</td><td>Used as the destination instruction when delivery_instructions is not set.</td></tr>\n<tr><td>carrier_insurance_amount</td><td>float</td><td>Declared value in USD. Used ahead of total_value.</td></tr>\n<tr><td>total_value</td><td>float</td><td>Declared value in USD when carrier_insurance_amount is not set.</td></tr>\n</tbody>\n</table>\n<p><strong>OnTrac values for delivery_confirmation:</strong></p>\n<ul>\n<li>NO_SIGNATURE</li>\n<li>SIGNATURE</li>\n<li>ADULT_SIGNATURE</li>\n<li>ADULT_SIGNATURE_RESTRICTED</li>\n</ul>\n<p>SIGNATURE_RESTRICTED is not offered by OnTrac and is treated as NO_SIGNATURE.</p>\n</details>\n\n### PrintCustom Object\n\nParameter | Type | Specification\n--------- | ----- | -----\nname | string | First part of text to print on label\nvalue | string | Last part of text to print on label\nbarcode | boolean | print the value as barcode (USPS only)\n\n<aside class=\"notice\">\n<strong>USPS: how many print_custom entries actually print.</strong> USPS does not print an unlimited number of custom fields, and which entries print is decided by the <em>service</em>, not the destination.\n<ul>\n<li><strong>Domestic labels</strong> (US destinations, including APO/FPO/DPO and the other US territories): only the first two entries that have a non-empty <code>value</code> print, each as a package reference number. Any further entries are dropped silently, with no error. Each value is trimmed to 30 characters.</li>\n<li><strong>USPS International services</strong> (non-US destination countries): only entries named <code>importer</code> and <code>exporter</code> print, as the customs form's importer and exporter reference. Every other entry prints nothing. Each reference is trimmed to 28 characters.</li>\n</ul>\nBecause the path is chosen by service, <code>importer</code> and <code>exporter</code> are only meaningful on USPS International services. APO/FPO/DPO ships on a domestic service, so naming an entry <code>importer</code> or <code>exporter</code> there prints it as a plain reference number and just uses up one of the two domestic slots.\n</aside>\n\n## Create a Shipment\n\n```shell\ncurl -X POST https://www.vanlo.com/api/v1/shipments \\\n  -H 'Authorization: Bearer VANLO_API_KEY' \\\n  -d 'shipment[to_address][name]=To Name' \\\n  -d 'shipment[to_address][street1]=To Street 1' \\\n  -d 'shipment[to_address][city]=To City' \\\n  -d 'shipment[to_address][state]=CA' \\\n  -d 'shipment[to_address][zip]=90277' \\\n  -d 'shipment[to_address][country]=US' \\\n  -d 'shipment[to_address][phone]=4151234567' \\\n  -d 'shipment[to_address][email]=to@example.com' \\\n  -d 'shipment[from_address][name]=From Name' \\\n  -d 'shipment[from_address][company]=From Company' \\\n  -d 'shipment[from_address][street1]=From Street 1' \\\n  -d 'shipment[from_address][street2]=From Street 2' \\\n  -d 'shipment[from_address][city]=From City' \\\n  -d 'shipment[from_address][state]=CA' \\\n  -d 'shipment[from_address][zip]=94104' \\\n  -d 'shipment[from_address][country]=US' \\\n  -d 'shipment[from_address][phone]=4157654321' \\\n  -d 'shipment[from_address][email]=from@example.com' \\\n  -d 'shipment[parcel][length]=8.1' \\\n  -d 'shipment[parcel][width]=7.2' \\\n  -d 'shipment[parcel][height]=6' \\\n  -d 'shipment[parcel][weight]=65.9'\n\n# OR reference previously created objects\n\ncurl -X POST https://www.vanlo.com/api/v1/shipments \\\n  -H 'Authorization: Bearer VANLO_API_KEY' \\\n  -d 'shipment[to_address][id]=adr_...' \\\n  -d 'shipment[from_address][id]=adr_...' \\\n  -d 'shipment[parcel][id]=prcl_...' \\\n  -d 'shipment[customs_info][id]=cstinfo_...'\n```\n\n```ruby\nrequire 'vanlo'\nVanlo.api_key = \"VANLO_API_KEY\"\n\nVanlo::Shipment.create(\n  to_address: {\n    name: 'To Name',\n    street1: 'To Street',\n    city: 'To City',\n    state: 'CA',\n    zip: '90277',\n    country: 'US',\n    phone: '4151234567',\n    email: 'to@example.com'\n  },\n  from_address: {\n    name: 'From Name',\n    company: 'From Company',\n    street1: 'From Street 1',\n    street2: 'From Street 2',\n    city: 'From City',\n    state: 'CA',\n    zip: '94104',\n    country: 'US',\n    phone: '4157654321',\n    email: 'from@example.com'\n  },\n  parcel: {\n    length: 8.1,\n    width: 7.2,\n    height: 6,\n    weight: 65.9\n  }\n)\n\n# OR reference previously created objects\n\nto_address = Vanlo::Address.create(...)\nfrom_address = Vanlo::Address.create(...)\nparcel = Vanlo::Parcel.create(...)\ncustoms_info = Vanlo::CustomsInfo.create(...)\n\nVanlo::Shipment.create(\n  to_address: to_address,\n  from_address: from_address,\n  parcel: parcel,\n  customs_info: customs_info\n)\n```\n\n```python\nimport vanlo\nvanlo.api_key = \"VANLO_API_KEY\"\n\nshipment = vanlo.Shipment.create(\n  to_address={\n    \"name\": 'To Name',\n    \"street1\": 'To Street',\n    \"city\": 'To City',\n    \"state\": 'CA',\n    \"zip\": '90277',\n    \"country\": 'US',\n    \"phone\": '4151234567',\n    \"email\": 'to@example.com'\n  },\n  from_address={\n    \"name\": 'From Name',\n    \"company\": 'From Company',\n    \"street1\": 'From Street 1',\n    \"street2\": 'From Street 2',\n    \"city\": 'From City',\n    \"state\": 'CA',\n    \"zip\": '94104',\n    \"country\": 'US',\n    \"phone\": '4157654321',\n    \"email\": 'from@example.com'\n  },\n  parcel={\n    \"length\": 8.1,\n    \"width\": 7.2,\n    \"height\": 6,\n    \"weight\": 65.9\n  }\n)\n\n# OR reference previously created objects\n\nto_address = vanlo.Address.create(...)\nfrom_address = vanlo.Address.create(...)\nparcel = vanlo.Parcel.create(...)\ncustoms_info = vanlo.CustomsInfo.create(...)\n\nshipment = vanlo.Shipment.create(\n  to_address=to_address,\n  from_address=from_address,\n  parcel=parcel,\n  customs_info=customs_info\n)\n```\n\n```php\nrequire_once(\"path/to/lib/vanlo.php\");\n\\Vanlo\\Vanlo::setApiKey(\"VANLO_API_KEY\");\n\n$shipment = \\Vanlo\\Shipment::create(array(\n  \"to_address\" => array(\n    'name' => 'To Name',\n    'street1' => 'To Street',\n    'city' => 'To City',\n    'state' => 'CA',\n    'zip' => '90277',\n    'country' => 'US',\n    'phone' => '4151234567',\n    'email' => 'to@example.com'\n  ),\n  \"from_address\" => array(\n    'name' => 'From Name',\n    'company' => 'From Company',\n    'street1' => 'From Street 1',\n    'street2' => 'From Street 2',\n    'city' => 'From City',\n    'state' => 'CA',\n    'zip' => '94104',\n    'country' => 'US',\n    'phone' => '4157654321',\n    'email' => 'from@example.com'\n  ),\n  \"parcel\" => array(\n    \"length\" => 8.1,\n    \"width\" => 7.2,\n    \"height\" => 6,\n    \"weight\" => 65.9\n  )\n));\n\n# OR reference previously created objects\n\n$to_address = \\Vanlo\\Address::create(...);\n$from_address = \\Vanlo\\Address::create(...);\n$parcel = \\Vanlo\\Parcel::create(...);\n$customs_info = \\Vanlo\\CustomsInfo::create(...);\n\n$shipment = \\Vanlo\\Shipment::create(array(\n  \"to_address\" => $to_address,\n  \"from_address\" => $from_address,\n  \"parcel\" => $parcel,\n  \"customs_info\" => $customs_info\n));\n```\n\n```csharp\nusing Vanlo;\nClientManager.SetCurrent(\"VANLO_API_KEY\");\n\nAddress fromAddress = new Address() {\n    name = \"From Name\",\n    company = \"From Company\",\n    street1 = \"From Street 1\",\n    street2 = \"From Street 2\",\n    city = \"From City\",\n    state = \"CA\",\n    zip = \"94104\",\n    country = \"US\",\n    phone = \"4157654321\",\n    email = \"from@example.com\"\n };\n\nAddress toAddress = new Address() {\n    name = \"To Name\",\n    street1 = \"To Street\",\n    city = \"To City\",\n    state = \"CA\",\n    zip = \"90277\",\n    country = \"US\",\n    phone = \"4151234567\",\n    email = \"to@example.com\"\n\n};\n\nParcel parcel = new Parcel() {\n    length = 8.1,\n    width = 7.2,\n    height = 6,\n    weight = 65.9\n};\n\nShipment shipment = new Shipment() {\n    from_address = fromAddress,\n    to_address = toAddress,\n    parcel = parcel,\n};\n\nshipment.Create();\n\n// OR reference previously created objects\n\nAddress to_address = Address.Create(...);\nAddress from_address = Address.Create(...);\nParcel parcel = Parcel.Create(...);\nCustomsInfo customs_info = CustomsInfo.Create(...);\n\nShipment shipment = new Shipment() {\n    to_address = to_address,\n    from_address = from_address,\n    parcel = parcel,\n    customs_info = customs_info\n};\n\nshipment.Create();\n```\n\n> The above command returns JSON structured like this:\n\n```json\n{\n  \"id\": \"shp_...\",\n  \"object\": \"Shipment\",\n    \"to_address\": {\n    \"id\": \"adr_...\",\n    \"object\": \"Address\",\n    \"name\": \"To Name\",\n    \"company\": null,\n    \"street1\": \"To Street\",\n    \"street2\": null,\n    \"city\": \"To City\",\n    \"state\": \"CA\",\n    \"zip\": \"90277\",\n    \"country\": \"US\",\n    \"phone\": \"4151234567\",\n    \"residential\": null,\n    \"email\": \"to@example.com\",\n    \"created_at\": \"2019-04-22T05:39:56Z\",\n    \"updated_at\": \"2019-04-22T05:39:56Z\"\n  },\n  \"from_address\": {\n    \"id\": \"adr_...\",\n    \"object\": \"Address\",\n    \"name\": \"From Name\",\n    \"company\": \"From Company\",\n    \"street1\": \"From Street 1\",\n    \"street2\": \"From Street 2\",\n    \"city\": \"From City\",\n    \"state\": \"CA\",\n    \"zip\": \"94104\",\n    \"country\": \"US\",\n    \"phone\": \"4157654321\",\n    \"email\": \"from@example.com\",\n    \"residential\": null,\n    \"created_at\": \"2019-04-22T05:39:57Z\",\n    \"updated_at\": \"2019-04-22T05:39:57Z\"\n  },\n  \"parcel\": {\n    \"id\": \"prcl_...\",\n    \"object\": \"Parcel\",\n    \"length\": 8.2,\n    \"width\": 7.1,\n    \"height\": 6.0,\n    \"predefined_package\": null,\n    \"weight\": 65.9,\n    \"created_at\": \"2019-04-22T05:39:57Z\",\n    \"updated_at\": \"2019-04-22T05:39:57Z\"\n  },\n  \"rates\": [\n    {\n      \"id\": \"rate_...\",\n      \"object\": \"Rate\",\n      \"created_at\": \"2019-04-22T05:39:57Z\",\n      \"carrier\": \"USPS\",\n      \"service\": \"GroundAdvantage\",\n      \"rate\": \"9.02\",\n      \"delivery_date\": null,\n      \"delivery_date_guaranteed\": false,\n      \"delivery_days\": 5\n    },\n    {\n      \"id\": \"rate_...\",\n      \"object\": \"Rate\",\n      \"created_at\": \"2019-04-22T05:39:57Z\",\n      \"carrier\": \"USPS\",\n      \"service\": \"Express\",\n      \"rate\": \"40.16\",\n      \"delivery_date\": null,\n      \"delivery_date_guaranteed\": false,\n      \"delivery_days\": null\n    },\n    {\n      \"id\": \"rate_...\",\n      \"object\": \"Rate\",\n      \"created_at\": \"2019-04-22T05:39:57Z\",\n      \"carrier\": \"USPS\",\n      \"service\": \"Priority\",\n      \"rate\": \"7.91\",\n      \"delivery_date\": null,\n      \"delivery_date_guaranteed\": false,\n      \"delivery_days\": 2\n    }\n  ],\n  \"insurance\": null,\n  \"selected_rate\": null,\n  \"postage_label\": null,\n  \"tracking_code\": null,\n  \"refund_status\": null,\n  \"created_at\": \"2019-04-22T05:40:57Z\",\n  \"updated_at\": \"2019-04-22T05:40:57Z\"\n}\n```\n\nShipments are the central objects of the Vanlo API. You will use them to send origin and destination addresses, parcel characteristics, and information for customs(when required).\n\nOnce a shipment is created we will attach the available shipping services (as Rate objects), and then a shipping label can be purchased by 'buying' one of the Rates.\n\nAn origin Address, destination Address, and Parcel are required for domestic rating and shipping. You will also have to include CustomsInfo whenever a shipment requires it (or include it on all shipments to simplify application logic).\n\nThe associated Rates, Tracker, and PostageLabel are generated by Vanlo and cannot be modified directly.\n\n### HTTP Request\n\n`POST https://www.vanlo.com/api/v1/shipments`\n\n### Create Shipment Request Parameters\n\nParameter | Type | Required? | Specification\n--------- | ----------- | --------- | ---------\nfrom_address | [\\<Address\\>](/#addresses) | Required | Your warehouse address\nto_address | [\\<Address\\>](/#addresses) | Required | Recipient address\nparcel | [\\<Parcel\\>](/#parcels) | Required | Package properties\ncustoms_info | [\\<CustomsInfo\\>](/#customsinfos) | Optional | Only required for international shipments and shipments to APO/DPO/FPO.\nservice | string | Optional | When given along with create_and_buy option, the label is purchased immediately.\ncarrier | string | Optional | The carrier name to ship. Default value is USPS.\ncarrier_accounts | [string...] | Optional | One or more Carrier Account ids to get rates from. Default value is your default USPS account.\nreference | string | Optional | Arbitrary string that you can use later to look up the shipment or filter all the shipments with the same reference to include in a ScanForm.\ncustom_id | string | Optional | Arbitrary but unique string that you can use later to look up the shipment.\nreturn_address | [\\<Address\\>](/#addresses) | Optional | Distinct return address if it differs from from_address.\nbuyer_address | [\\<Address\\>](/#addresses) | Optional | Distinct  buyer address if it differs from to_address.\nparcels | [[\\<Parcel\\>](/#parcels)...] | Optional | FedEx only. Pass multiple parcel objects instead of a single object in the parcel field to create a multi-piece shipment.\n\n\n## Buy a Shipment\n\n```shell\ncurl -X POST https://www.vanlo.com/api/v1/shipments/shp_.../buy \\\n  -H 'Authorization: Bearer VANLO_API_KEY' \\\n  -d 'rate[id]=rate_...'\n```\n\n```ruby\nrequire 'vanlo'\nVanlo.api_key = \"VANLO_API_KEY\"\n\nshipment = Vanlo::Shipment.retrieve(\"shp_...\")\nshipment.buy(rate: shipment.lowest_rate)\n```\n\n```python\nimport vanlo\nvanlo.api_key = \"VANLO_API_KEY\"\n\nshipment = vanlo.Shipment.retrieve(\"shp_...\")\nshipment.buy(rate=shipment.lowest_rate())\n```\n\n```php\nrequire_once(\"path/to/lib/vanlo.php\");\n\\Vanlo\\Vanlo::setApiKey(\"VANLO_API_KEY\");\n\n$shipment = \\Vanlo\\Shipment::retrieve(\"shp_...\");\n$shipment->buy(array(\n  'rate'      => $shipment->lowest_rate()\n));\n```\n\n```csharp\nusing Vanlo;\nClientManager.SetCurrent(\"VANLO_API_KEY\");\n\nShipment shipment = Shipment.Retrieve(\"shp...\");\nRate lowestRate = shipment.LowestRate();\n\nshipment.Buy(lowestRate);\n```\n\n> The above command returns JSON structured like this:\n\n```json\n{\n  \"id\": \"shp_...\",\n  \"object\": \"Shipment\",\n  \"to_address\": {\n    \"id\": \"adr_...\",\n    \"object\": \"Address\",\n    \"name\": \"To Name\",\n    \"company\": null,\n    \"street1\": \"To Street\",\n    \"street2\": null,\n    \"city\": \"To City\",\n    \"state\": \"CA\",\n    \"zip\": \"90277\",\n    \"country\": \"US\",\n    \"phone\": \"4151234567\",\n    \"residential\": null,\n    \"email\": \"to@example.com\",\n    \"created_at\": \"2019-04-22T05:39:56Z\",\n    \"updated_at\": \"2019-04-22T05:39:56Z\"\n  },\n  \"from_address\": {\n    \"id\": \"adr_...\",\n    \"object\": \"Address\",\n    \"name\": \"From Name\",\n    \"company\": \"From Company\",\n    \"street1\": \"From Street 1\",\n    \"street2\": \"From Street 2\",\n    \"city\": \"From City\",\n    \"state\": \"CA\",\n    \"zip\": \"94104\",\n    \"country\": \"US\",\n    \"phone\": \"4157654321\",\n    \"email\": \"from@example.com\",\n    \"residential\": null,\n    \"created_at\": \"2019-04-22T05:39:57Z\",\n    \"updated_at\": \"2019-04-22T05:39:57Z\"\n  },\n  \"parcel\": {\n    \"id\": \"prcl_...\",\n    \"object\": \"Parcel\",\n    \"length\": 8.2,\n    \"width\": 7.1,\n    \"height\": 6,\n    \"predefined_package\": null,\n    \"weight\": 65.9,\n    \"created_at\": \"2019-04-22T05:39:57Z\",\n    \"updated_at\": \"2019-04-22T05:39:57Z\"\n  },\n  \"rates\": [\n    {\n      \"id\": \"rate_...\",\n      \"object\": \"Rate\",\n      \"created_at\": \"2019-04-22T05:39:57Z\",\n      \"carrier\": \"USPS\",\n      \"service\": \"GroundAdvantage\",\n      \"rate\": \"9.02\",\n      \"delivery_date\": null,\n      \"delivery_date_guaranteed\": false,\n      \"delivery_days\": 5\n    },\n    {\n      \"id\": \"rate_...\",\n      \"object\": \"Rate\",\n      \"created_at\": \"2019-04-22T05:39:57Z\",\n      \"carrier\": \"USPS\",\n      \"service\": \"Express\",\n      \"rate\": \"40.16\",\n      \"delivery_date\": null,\n      \"delivery_date_guaranteed\": false,\n      \"delivery_days\": null\n    },\n    {\n      \"id\": \"rate_...\",\n      \"object\": \"Rate\",\n      \"created_at\": \"2019-04-22T05:39:57Z\",\n      \"carrier\": \"USPS\",\n      \"service\": \"Priority\",\n      \"rate\": \"7.91\",\n      \"delivery_date\": null,\n      \"delivery_date_guaranteed\": false,\n      \"delivery_days\": 2\n    }\n  ],\n  \"selected_rate\": {\n    \"id\": \"rate_...\",\n    \"object\": \"Rate\",\n    \"created_at\": \"2019-04-22T05:40:57Z\",\n    \"carrier\": \"USPS\",\n    \"service\": \"Priority\",\n    \"rate\": \"7.91\",\n    \"delivery_date\": null,\n    \"delivery_date_guaranteed\": false,\n    \"delivery_days\": 2\n  },\n  \"postage_label\": {\n    \"object\": \"PostageLabel\",\n    \"created_at\": \"2019-04-22T05:40:57Z\",\n    \"updated_at\": \"2019-04-22T05:40:57Z\",\n    \"id\": \"pl_...\",\n    \"integrated_form\": null,\n    \"label_date\": \"2019-04-23\",\n    \"label_epl2_url\": null,\n    \"label_file_type\": \"image/png\",\n    \"label_pdf_url\": null,\n    \"label_resolution\": null,\n    \"label_size\": null,\n    \"label_type\": null,\n    \"label_url\": \"https://....png\",\n    \"label_zpl_url\": null\n  },\n  \"tracker\": {\n    \"object\": \"Tracker\",\n    \"created_at\": \"2019-04-22T05:40:57Z\",\n    \"updated_at\": \"2019-04-22T05:40:57Z\",\n    \"id\": \"trk_...\",\n    \"shipment_id\": \"shp_...\",\n    \"status\": null,\n    \"tracking_code\": \"9405500205903028777744\",\n    \"tracking_details\": [],\n    \"public_url\": null\n  },\n  \"insurance\": null,\n  \"options\": {\n    \"label_format\": \"png\"\n  },\n  \"created_at\": \"2019-04-22T05:40:57Z\",\n  \"updated_at\": \"2019-04-22T05:40:57Z\"\n}\n```\n\nTo purchase a Shipment you only need to specify the Rate to purchase. This operation creates a Tracker and PostageLabel and returns the updated Shipment. The default image format of the associated PostageLabel is PNG. To change this default see the label_format option.\n\n### HTTP Request\n\n`POST https://www.vanlo.com/api/v1/shipments/:id/buy  `\n\n### Buy Shipment Request Parameters\n\nParameter | Type | Specification\n--------- | ----- | -----\nrate | object | Rate object to purchase, must include id (e.g. {id: \"rate_...\"})\n\n## Refund a Shipment\n\n```shell\ncurl -X POST https://www.vanlo.com/api/v1/shipments/shp_.../refund \\\n  -H 'Authorization: Bearer VANLO_API_KEY'\n```\n\n```ruby\nrequire 'vanlo'\nVanlo.api_key = \"VANLO_API_KEY\"\n\nshipment = Vanlo::Shipment.retrieve(\"shp_...\")\nshipment.refund\n```\n\n```python\nimport vanlo\nvanlo.api_key = \"VANLO_API_KEY\"\n\nshipment = vanlo.Shipment.retrieve(\"shp_...\")\nshipment.refund()\n```\n\n```php\nrequire_once(\"path/to/lib/vanlo.php\");\n\\Vanlo\\Vanlo::setApiKey(\"VANLO_API_KEY\");\n\n$shipment = \\Vanlo\\Shipment::retrieve(\"shp_...\");\n$shipment->refund();\n```\n\n```csharp\nusing Vanlo;\nClientManager.SetCurrent(\"VANLO_API_KEY\");\n\nShipment ship ment = Shipment.Retrieve(\"shp...\");\nshipment.Refund();\n```\n\n> The above command returns JSON structured like this:\n\n```json\n{\n  \"id\": \"shp_...\",\n  \"object\": \"Shipment\",\n  \"to_address\": {\n    \"id\": \"adr_...\",\n    \"object\": \"Address\",\n    \"name\": \"To Name\",\n    \"company\": null,\n    \"street1\": \"To Street\",\n    \"street2\": null,\n    \"city\": \"To City\",\n    \"state\": \"CA\",\n    \"zip\": \"90277\",\n    \"country\": \"US\",\n    \"phone\": \"4151234567\",\n    \"residential\": null,\n    \"email\": \"to@example.com\",\n    \"created_at\": \"2019-04-22T05:39:56Z\",\n    \"updated_at\": \"2019-04-22T05:39:56Z\"\n  },\n  \"from_address\": {\n    \"id\": \"adr_...\",\n    \"object\": \"Address\",\n    \"name\": \"From Name\",\n    \"company\": \"From Company\",\n    \"street1\": \"From Street 1\",\n    \"street2\": \"From Street 2\",\n    \"city\": \"From City\",\n    \"state\": \"CA\",\n    \"zip\": \"94104\",\n    \"country\": \"US\",\n    \"phone\": \"4157654321\",\n    \"email\": \"from@example.com\",\n    \"residential\": null,\n    \"created_at\": \"2019-04-22T05:39:57Z\",\n    \"updated_at\": \"2019-04-22T05:39:57Z\"\n  },\n  \"parcel\": {\n    \"id\": \"prcl_...\",\n    \"object\": \"Parcel\",\n    \"length\": 8.2,\n    \"width\": 7.1,\n    \"height\": 6.0,\n    \"predefined_package\": null,\n    \"weight\": 65.9,\n    \"created_at\": \"2019-04-22T05:39:57Z\",\n    \"updated_at\": \"2019-04-22T05:39:57Z\"\n  },\n  \"rates\": [\n    {\n      \"id\": \"rate_...\",\n      \"object\": \"Rate\",\n      \"created_at\": \"2019-04-22T05:39:57Z\",\n      \"carrier\": \"USPS\",\n      \"service\": \"GroundAdvantage\",\n      \"rate\": \"9.02\",\n      \"delivery_date\": null,\n      \"delivery_date_guaranteed\": false,\n      \"delivery_days\": 5\n    },\n    {\n      \"id\": \"rate_...\",\n      \"object\": \"Rate\",\n      \"created_at\": \"2019-04-22T05:39:57Z\",\n      \"carrier\": \"USPS\",\n      \"service\": \"Express\",\n      \"rate\": \"40.16\",\n      \"delivery_date\": null,\n      \"delivery_date_guaranteed\": false,\n      \"delivery_days\": null\n    },\n    {\n      \"id\": \"rate_...\",\n      \"object\": \"Rate\",\n      \"created_at\": \"2019-04-22T05:39:57Z\",\n      \"carrier\": \"USPS\",\n      \"service\": \"Priority\",\n      \"rate\": \"7.91\",\n      \"delivery_date\": null,\n      \"delivery_date_guaranteed\": false,\n      \"delivery_days\": 2\n    }\n  ],\n  \"insurance\": null,\n  \"selected_rate\": null,\n  \"postage_label\": null,\n  \"tracking_code\": null,\n  \"refund_status\": \"submitted\",\n  \"created_at\": \"2019-04-22T05:40:57Z\",\n  \"updated_at\": \"2019-04-22T05:40:57Z\"\n}\n```\n\nOnce the refund has been submitted, refund_status attribute of the Shipment will be populated with one of the possible values: \"submitted\", \"refunded\", \"rejected\".\n\n### HTTP Request\n\n`POST https://www.vanlo.com/api/v1/shipments/:id/refund`\n\n### Refund Shipment Request Parameters\n\nParameter | Type | Specification\n--------- | ----- | -----\nid | string | Unique identifier of the shipment to refund\n\n## List Shipments\n\n```shell\ncurl -X GET https://www.vanlo.com/api/v1/shipments \\\n  -H 'Authorization: Bearer VANLO_API_KEY' \\\n  -d 'page_size=5'\n```\n\n```ruby\nrequire 'vanlo'\nVanlo.api_key = 'VANLO_API_KEY'\n\nVanlo::Shipment.all(page_size: 5)\n```\n\n```python\nimport vanlo\nvanlo.api_key = 'VANLO_API_KEY'\n\nvanlo.Shipment.all(page_size=5)\n```\n\n```php\nrequire_once('/path/to/lib/vanlo.php');\n\\Vanlo\\Vanlo::setApiKey('VANLO_API_KEY');\n\n$shipments = \\Vanlo\\Shipment::all(array('page_size' => 5));\n```\n\n```csharp\nusing Vanlo;\nClientManager.SetCurrent(\"VANLO_API_KEY\");\n\nvar listParams = new Dictionary<string, object>() {\n    { \"page_size\", 5 }\n};\n\nShipmentList shipmentList = Shipment.List(listParams);\n```\n\n> The above command returns JSON structured like this:\n\n```json\n{\n  \"shipments\": [\n    {\n      \"id\": \"shp_...\",\n      \"object\": \"Shipment\",\n      \"to_address\": {\n        \"id\": \"adr_...\",\n        \"object\": \"Address\",\n        \"name\": \"To Name\",\n        \"street1\": \"To Street\",\n        \"city\": \"To City\",\n        \"state\": \"CA\",\n        \"zip\": \"90277\",\n        \"country\": \"US\"\n      },\n      \"from_address\": {\n        \"id\": \"adr_...\",\n        \"object\": \"Address\",\n        \"name\": \"From Name\",\n        \"street1\": \"From Street 1\",\n        \"city\": \"From City\",\n        \"state\": \"CA\",\n        \"zip\": \"94104\",\n        \"country\": \"US\"\n      },\n      \"parcel\": {\n        \"id\": \"prcl_...\",\n        \"object\": \"Parcel\",\n        \"length\": 8.2,\n        \"width\": 7.1,\n        \"height\": 6.0,\n        \"weight\": 65.9\n      },\n      \"rates\": [],\n      \"insurance\": null,\n      \"selected_rate\": null,\n      \"postage_label\": null,\n      \"tracking_code\": null,\n      \"refund_status\": null,\n      \"created_at\": \"2019-04-22T05:40:57Z\",\n      \"updated_at\": \"2019-04-22T05:40:57Z\"\n    }\n  ],\n  \"has_more\": true\n}\n```\n\nThe Shipment List is a paginated list of all Shipment objects associated with the given API key. The `has_more` attribute indicates whether additional pages can be requested. The recommended way of paginating is to use either the `before_id` or `after_id` parameter to specify where the next page begins.\n\n### HTTP Request\n\n`GET https://www.vanlo.com/api/v1/shipments`\n\n### List Shipments Request Parameters\n\nParameter | Type | Specification\n--------- | ----- | -----\npurchased | boolean | When present, only return purchased shipments (shipments with a tracking code)\nbefore_id | string | Return shipments created before this id\nafter_id | string | Return shipments created after this id\nstart_datetime | datetime | Only return shipments created after this timestamp\nend_datetime | datetime | Only return shipments created before this timestamp\npage_size | integer | Number of shipments to return per page (default 20, max 100)\n\n## Retrieve a Shipment\n\n```shell\ncurl -X GET https://www.vanlo.com/api/v1/shipments/shp_... \\\n  -H 'Authorization: Bearer VANLO_API_KEY'\n```\n\n```ruby\nrequire 'vanlo'\nVanlo.api_key = 'VANLO_API_KEY'\n\nVanlo::Shipment.retrieve('shp_...')\n```\n\n```python\nimport vanlo\nvanlo.api_key = 'VANLO_API_KEY'\n\nvanlo.Shipment.retrieve('shp_...')\n```\n\n```php\nrequire_once('/path/to/lib/vanlo.php');\n\\Vanlo\\Vanlo::setApiKey('VANLO_API_KEY');\n\n$shipment = \\Vanlo\\Shipment::retrieve('shp_...');\n```\n\n```csharp\nusing Vanlo;\nClientManager.SetCurrent(\"VANLO_API_KEY\");\n\nShipment shipment = Shipment.Retrieve(\"shp_...\");\n```\n\n> The above command returns JSON structured like this:\n\n```json\n{\n  \"id\": \"shp_...\",\n  \"object\": \"Shipment\",\n  \"to_address\": {\n    \"id\": \"adr_...\",\n    \"object\": \"Address\",\n    \"name\": \"To Name\",\n    \"company\": null,\n    \"street1\": \"To Street\",\n    \"street2\": null,\n    \"city\": \"To City\",\n    \"state\": \"CA\",\n    \"zip\": \"90277\",\n    \"country\": \"US\",\n    \"phone\": \"4151234567\",\n    \"residential\": null,\n    \"email\": \"to@example.com\",\n    \"created_at\": \"2019-04-22T05:39:56Z\",\n    \"updated_at\": \"2019-04-22T05:39:56Z\"\n  },\n  \"from_address\": {\n    \"id\": \"adr_...\",\n    \"object\": \"Address\",\n    \"name\": \"From Name\",\n    \"company\": \"From Company\",\n    \"street1\": \"From Street 1\",\n    \"street2\": \"From Street 2\",\n    \"city\": \"From City\",\n    \"state\": \"CA\",\n    \"zip\": \"94104\",\n    \"country\": \"US\",\n    \"phone\": \"4157654321\",\n    \"email\": \"from@example.com\",\n    \"residential\": null,\n    \"created_at\": \"2019-04-22T05:39:57Z\",\n    \"updated_at\": \"2019-04-22T05:39:57Z\"\n  },\n  \"parcel\": {\n    \"id\": \"prcl_...\",\n    \"object\": \"Parcel\",\n    \"length\": 8.2,\n    \"width\": 7.1,\n    \"height\": 6.0,\n    \"predefined_package\": null,\n    \"weight\": 65.9,\n    \"created_at\": \"2019-04-22T05:39:57Z\",\n    \"updated_at\": \"2019-04-22T05:39:57Z\"\n  },\n  \"rates\": [\n    {\n      \"id\": \"rate_...\",\n      \"object\": \"Rate\",\n      \"created_at\": \"2019-04-22T05:39:57Z\",\n      \"carrier\": \"USPS\",\n      \"service\": \"Priority\",\n      \"rate\": \"7.91\",\n      \"delivery_date\": null,\n      \"delivery_date_guaranteed\": false,\n      \"delivery_days\": 2\n    }\n  ],\n  \"insurance\": null,\n  \"selected_rate\": null,\n  \"postage_label\": null,\n  \"tracking_code\": null,\n  \"refund_status\": null,\n  \"created_at\": \"2019-04-22T05:40:57Z\",\n  \"updated_at\": \"2019-04-22T05:40:57Z\"\n}\n```\n\nRetrieve a Shipment by its id. You can also look up a Shipment by its `tracking_code`, `custom_id`, or `reference`.\n\n### HTTP Request\n\n`GET https://www.vanlo.com/api/v1/shipments/:id`\n\n### Retrieve Shipment Request Parameters\n\nParameter | Type | Specification\n--------- | ----- | -----\nid | string | Unique identifier of the shipment, or a tracking code, custom_id, or reference\n\n## Dispute a Transaction\n\n<aside class=\"notice\">\nYou can dispute only the adjustments USPS makes after a shipment - its reassessments of weight, zone, or dimensions. Other charges, such as the original shipment purchase, a duplicate charge, or a failed refund, are not USPS adjustments and cannot be disputed through USPS. They return an error (see below).\n</aside>\n\n```shell\ncurl -X POST https://www.vanlo.com/api/v1/transactions/txn_.../dispute \\\n  -H 'Authorization: Bearer VANLO_API_KEY' \\\n  -d 'reason=INCORRECT_ASSESSED_WEIGHT' \\\n  -d 'description=Package weighed 4 lbs, USPS billed for 7 lbs'\n```\n\n```ruby\nrequire 'vanlo'\nVanlo.api_key = 'VANLO_API_KEY'\n\ntransaction = Vanlo::Transaction.retrieve('txn_...')\ntransaction.dispute(\n  reason: 'INCORRECT_ASSESSED_WEIGHT',\n  description: 'Package weighed 4 lbs, USPS billed for 7 lbs'\n)\n```\n\n```python\nimport vanlo\nvanlo.api_key = 'VANLO_API_KEY'\n\ntransaction = vanlo.Transaction.retrieve('txn_...')\ntransaction.dispute(\n    reason='INCORRECT_ASSESSED_WEIGHT',\n    description='Package weighed 4 lbs, USPS billed for 7 lbs'\n)\n```\n\n```php\nrequire_once('/path/to/lib/vanlo.php');\n\\Vanlo\\Vanlo::setApiKey('VANLO_API_KEY');\n\n$transaction = \\Vanlo\\Transaction::retrieve('txn_...');\n$transaction->dispute(array(\n  'reason' => 'INCORRECT_ASSESSED_WEIGHT',\n  'description' => 'Package weighed 4 lbs, USPS billed for 7 lbs'\n));\n```\n\n```csharp\nusing Vanlo;\nClientManager.SetCurrent(\"VANLO_API_KEY\");\n\nTransaction transaction = Transaction.Retrieve(\"txn_...\");\ntransaction.Dispute(\"INCORRECT_ASSESSED_WEIGHT\", \"Package weighed 4 lbs, USPS billed for 7 lbs\");\n```\n\n> The above command returns JSON structured like this:\n\n```json\n{\n  \"id\": 123,\n  \"dispute_id\": \"DSP-2026-001\",\n  \"status\": \"NEW\",\n  \"reason\": \"INCORRECT_ASSESSED_WEIGHT\",\n  \"message\": \"Dispute submitted to USPS\"\n}\n```\n\nIf USPS makes a post-shipment adjustment on your account - a reassessment of weight, zone, or dimensions - you can dispute it. Vanlo submits the dispute to the USPS Disputes API on your behalf. If USPS accepts the dispute, the refund is credited to your account automatically.\n\nUSPS only reviews disputes for the adjustments it makes after a shipment. When USPS issues such an adjustment, it sends a dispute reference, and Vanlo uses that reference to match the dispute to the charge. A charge with no USPS dispute reference - for example the original shipment purchase, a duplicate charge, or a failed refund - is not a USPS adjustment, so USPS will not review it. Disputing one returns a `400` error: `This charge can't be disputed through USPS. USPS only reviews disputes for adjustments it makes after a shipment (its reassessments of weight, zone, or dimensions).`\n\nA transaction can be disputed only once. A second attempt returns a `400` error: `Transaction already disputed by user`.\n\n### HTTP Request\n\n`POST https://www.vanlo.com/api/v1/transactions/:id/dispute`\n\n### Dispute Transaction Request Parameters\n\nParameter | Type | Required? | Specification\n--------- | ----- | --------- | ---------\nreason | string | Required | Reason for the dispute. USPS accepts `INCORRECT_ASSESSED_WEIGHT`, `INCORRECT_ASSESSED_ZONE`, `INCORRECT_ASSESSED_PACKAGING`, `INCORRECT_ASSESSED_DIMENSIONS`, `INCORRECT_ASSESSED_DUPLICATE`, `UNDOCUMENTED`, `NONCOMPLIANT_DIMENSIONS`, `RETURN_LABEL`, `MISSHIPPED`, or `OTHER`.\ndescription | string | Optional | Free-text description providing more context for the dispute. Defaults to the reason when omitted.\n\n### Shipment Errors\n\nBeyond the [common errors](/#errors) shared by every endpoint (authentication, parameter\nvalidation, rate limiting, and server errors), shipment create, buy, and refund can return:\n\nStatus | Code | When it happens | How to handle\n------ | ---- | --------------- | -------------\n402 | `PAYMENT_REQUIRED` | Buying a label with a balance that is too low. | Add funds, then retry.\n422 | `INVALID_OPTIONS` | An options value is not allowed, for example an unsupported service or option for the chosen carrier. | Use a valid option, then retry.\n424 | `EXTERNAL_SERVICE_ERROR` | The carrier rejected or failed the shipment during create, buy, or refund - bad dimensions, a service mismatch, an address the carrier would not accept, an expired rate, or the carrier being down. On refund, it means the carrier could not cancel this label. | Read `error.message` for the carrier's reason. Fix the shipment if it is an input problem, otherwise retry with backoff.\n\nTwo common errors are worth calling out here: a concurrent buy of the same shipment returns\n`LOCKED` 429, and a create-and-buy that runs past the internal timeout returns `TIMEOUT_EXCEEDED`\n504 - retry both with backoff. See [Errors](/#errors) for the full list and how to handle each one."
    },
    {
      "id": "rates",
      "title": "Rates",
      "content": "# Rates\n\n## Get a Rate Quote\n\n```shell\ncurl -X POST https://www.vanlo.com/api/v1/rate \\\n  -H 'Authorization: Bearer VANLO_API_KEY' \\\n  -d 'shipment[to_address][name]=To Name' \\\n  -d 'shipment[to_address][street1]=To Street' \\\n  -d 'shipment[to_address][city]=To City' \\\n  -d 'shipment[to_address][state]=CA' \\\n  -d 'shipment[to_address][zip]=90277' \\\n  -d 'shipment[to_address][country]=US' \\\n  -d 'shipment[from_address][name]=From Name' \\\n  -d 'shipment[from_address][street1]=From Street 1' \\\n  -d 'shipment[from_address][city]=From City' \\\n  -d 'shipment[from_address][state]=CA' \\\n  -d 'shipment[from_address][zip]=94104' \\\n  -d 'shipment[from_address][country]=US' \\\n  -d 'shipment[parcel][length]=8.1' \\\n  -d 'shipment[parcel][width]=7.2' \\\n  -d 'shipment[parcel][height]=6' \\\n  -d 'shipment[parcel][weight]=65.9'\n```\n\n```ruby\nrequire 'vanlo'\nVanlo.api_key = 'VANLO_API_KEY'\n\nVanlo::Rate.create(\n  to_address: {\n    name: 'To Name',\n    street1: 'To Street',\n    city: 'To City',\n    state: 'CA',\n    zip: '90277',\n    country: 'US'\n  },\n  from_address: {\n    name: 'From Name',\n    street1: 'From Street 1',\n    city: 'From City',\n    state: 'CA',\n    zip: '94104',\n    country: 'US'\n  },\n  parcel: {\n    length: 8.1,\n    width: 7.2,\n    height: 6,\n    weight: 65.9\n  }\n)\n```\n\n```python\nimport vanlo\nvanlo.api_key = 'VANLO_API_KEY'\n\nvanlo.Rate.create(\n    to_address={\n        \"name\": 'To Name',\n        \"street1\": 'To Street',\n        \"city\": 'To City',\n        \"state\": 'CA',\n        \"zip\": '90277',\n        \"country\": 'US'\n    },\n    from_address={\n        \"name\": 'From Name',\n        \"street1\": 'From Street 1',\n        \"city\": 'From City',\n        \"state\": 'CA',\n        \"zip\": '94104',\n        \"country\": 'US'\n    },\n    parcel={\n        \"length\": 8.1,\n        \"width\": 7.2,\n        \"height\": 6,\n        \"weight\": 65.9\n    }\n)\n```\n\n```php\nrequire_once('/path/to/lib/vanlo.php');\n\\Vanlo\\Vanlo::setApiKey('VANLO_API_KEY');\n\n$rates = \\Vanlo\\Rate::create(array(\n  'to_address' => array(\n    'name' => 'To Name',\n    'street1' => 'To Street',\n    'city' => 'To City',\n    'state' => 'CA',\n    'zip' => '90277',\n    'country' => 'US'\n  ),\n  'from_address' => array(\n    'name' => 'From Name',\n    'street1' => 'From Street 1',\n    'city' => 'From City',\n    'state' => 'CA',\n    'zip' => '94104',\n    'country' => 'US'\n  ),\n  'parcel' => array(\n    'length' => 8.1,\n    'width' => 7.2,\n    'height' => 6,\n    'weight' => 65.9\n  )\n));\n```\n\n```csharp\nusing Vanlo;\nClientManager.SetCurrent(\"VANLO_API_KEY\");\n\nvar rateParams = new Dictionary<string, object>() {\n    { \"to_address\", new Dictionary<string, object>() {\n        { \"name\", \"To Name\" },\n        { \"street1\", \"To Street\" },\n        { \"city\", \"To City\" },\n        { \"state\", \"CA\" },\n        { \"zip\", \"90277\" },\n        { \"country\", \"US\" }\n    } },\n    { \"from_address\", new Dictionary<string, object>() {\n        { \"name\", \"From Name\" },\n        { \"street1\", \"From Street 1\" },\n        { \"city\", \"From City\" },\n        { \"state\", \"CA\" },\n        { \"zip\", \"94104\" },\n        { \"country\", \"US\" }\n    } },\n    { \"parcel\", new Dictionary<string, object>() {\n        { \"length\", 8.1 },\n        { \"width\", 7.2 },\n        { \"height\", 6.0 },\n        { \"weight\", 65.9 }\n    } }\n};\n\nList<Rate> rates = Rate.Create(rateParams);\n```\n\n> The above command returns JSON structured like this:\n\n```json\n{\n  \"rates\": [\n    {\n      \"id\": \"rate_...\",\n      \"object\": \"Rate\",\n      \"created_at\": \"2019-04-22T05:39:57Z\",\n      \"carrier\": \"USPS\",\n      \"service\": \"GroundAdvantage\",\n      \"rate\": \"9.02\",\n      \"delivery_date\": null,\n      \"delivery_date_guaranteed\": false,\n      \"delivery_days\": 5\n    },\n    {\n      \"id\": \"rate_...\",\n      \"object\": \"Rate\",\n      \"created_at\": \"2019-04-22T05:39:57Z\",\n      \"carrier\": \"USPS\",\n      \"service\": \"Express\",\n      \"rate\": \"40.16\",\n      \"delivery_date\": null,\n      \"delivery_date_guaranteed\": false,\n      \"delivery_days\": null\n    },\n    {\n      \"id\": \"rate_...\",\n      \"object\": \"Rate\",\n      \"created_at\": \"2019-04-22T05:39:57Z\",\n      \"carrier\": \"USPS\",\n      \"service\": \"Priority\",\n      \"rate\": \"7.91\",\n      \"delivery_date\": null,\n      \"delivery_date_guaranteed\": false,\n      \"delivery_days\": 2\n    }\n  ]\n}\n```\n\nGet rate quotes for a shipment without creating a persisted Shipment object. This is useful when you only need pricing information and do not intend to purchase a label immediately.\n\nThe request parameters are nested under `shipment[...]`, matching the same format used to create a shipment.\n\n### HTTP Request\n\n`POST https://www.vanlo.com/api/v1/rate`\n\n### Rate Quote Request Parameters\n\nParameter | Type | Required? | Specification\n--------- | ----------- | --------- | ---------\nshipment[to_address] | [\\<Address\\>](/#addresses) | Required | Destination address (zip and country are required)\nshipment[from_address] | [\\<Address\\>](/#addresses) | Required | Origin address (zip and country are required)\nshipment[parcel] | [\\<Parcel\\>](/#parcels) | Required | Package dimensions and weight\nshipment[carrier] | string | Optional | Filter rates to a specific carrier\nshipment[carrier_accounts] | [string...] | Optional | One or more Carrier Account ids to get rates from\nshipment[options] | [\\<Options\\>](/#options-object) | Optional | Shipping options that may affect rates (e.g. special_rates_eligibility, hazmat)\n\n### Rate Errors\n\nBeyond the [common errors](/#errors) shared by every endpoint, rate quoting can return:\n\nStatus | Code | When it happens | How to handle\n------ | ---- | --------------- | -------------\n422 | `INVALID_OPTIONS` | An options value is not allowed for the requested shipment. | Use a valid option, then retry.\n\nA rating problem with one carrier does not fail the request - that carrier is simply left out\nof the results. When no carrier can rate the shipment at all, the request returns the common\n`NOT_FOUND` 404 (no rates found); an input the API cannot rate returns `BAD_REQUEST` 400. See\n[Errors](/#errors) for the full list and how to handle each one."
    },
    {
      "id": "addresses",
      "title": "Addresses",
      "content": "# Addresses\n\n## Address Object\n\nParameter | Type | Specification\n--------- | ----- | -----\nid | string | Unique identifier, begins with \"adr_\"\nobject | string | \"Address\"\nname | string | Name of the person or contact\ncompany | string | Company name associated with the address\nstreet1 | string | First line of the street address\nstreet2 | string | Second line of the street address\ncity | string | City\nstate | string | State or province (2-letter code for US, CA, AU)\nzip | string | Zip or postal code\ncountry | string | Two-letter ISO 3166 country code\nphone | string | Phone number associated with the address\nemail | string | Email address\nresidential | boolean | Whether the address is a residential address\nverifications | object | Address verification results, if requested\ncreated_at | datetime | When the address was created\nupdated_at | datetime | When the address was last updated\n\n## Create an address\n\n```shell\ncurl -X POST https://www.vanlo.com/api/v1/addresses \\\n  -H 'Authorization: Bearer VANLO_API_KEY' \\\n  -d \"address[company]=VANLO\" \\\n  -d \"address[street1]=123 MONTGOMERY ST\" \\\n  -d \"address[street2]=STE 400\" \\\n  -d \"address[city]=SAN FRANCISCO\" \\\n  -d \"address[state]=CA\" \\\n  -d \"address[zip]=94104\" \\\n  -d \"address[country]=US\" \\\n  -d \"address[phone]=4151234567\"\n```\n\n```ruby\nrequire 'vanlo'\nVanlo.api_key = 'VANLO_API_KEY'\n\nVanlo::Address.create(\n  company: \"VANLO\",\n  street1: \"123 MONTGOMERY ST\",\n  street2: \"STE 400\",\n  city: \"SAN FRANCISCO\",\n  state: \"CA\",\n  zip: \"94104\",\n  country: \"US\",\n  phone: \"4151234567\"\n)\n```\n\n```python\nimport vanlo\nvanlo.api_key = 'VANLO_API_KEY'\n\nvanlo.Address.create(\n  company=\"VANLO\",\n  street1=\"123 MONTGOMERY ST\",\n  street2=\"STE 400\",\n  city=\"SAN FRANCISCO\",\n  state=\"CA\",\n  zip=\"94104\",\n  country=\"US\",\n  phone=\"4151234567\"\n)\n```\n\n```php\nrequire_once(\"/path/to/lib/vanlo.php\");\n\\Vanlo\\Vanlo::setApiKey('VANLO_API_KEY');\n\n$address_params = array(\n  \"company\" => \"VANLO\",\n  \"street1\" => \"123 MONTGOMERY ST\",\n  \"street2\" => \"STE 400\",\n  \"city\" => \"SAN FRANCISCO\",\n  \"state\" => \"CA\",\n  \"zip\" => \"94104\",\n  \"country\" => \"US\",\n  \"phone\" => \"4151234567\"\n);\n\n\\Vanlo\\Address::create($address_params);\n```\n\n```csharp\nusing Vanlo;\nClientManager.SetCurrent(\"VANLO_API_KEY\");\n\nAddress address = Address.Create(\n    new Dictionary<string, object>() {\n        { \"street1\", \"123 MONTGOMERY ST\" },\n        { \"street2\", \"STE 400\" },\n        { \"city\", \"SAN FRANCISCO\" },\n        { \"state\", \"CA\" },\n        { \"zip\", \"94104\" },\n        { \"country\", \"US\" },\n        { \"phone\", \"415-123-4567\" }\n    }\n);\n```\n\n> The above command returns JSON structured like this:\n\n```json\n{\n  \"id\": \"adr_...\",\n  \"object\": \"Address\",\n  \"created_at\": \"2019-09-06T12:01:52.503Z\",\n  \"updated_at\": \"2019-09-06T12:01:52.503Z\",\n  \"name\": null,\n  \"company\": \"VANLO\",\n  \"street1\": \"123 MONTGOMERY ST\",\n  \"street2\": \"STE 400\",\n  \"city\": \"SAN FRANCISCO\",\n  \"state\": \"CA\",\n  \"zip\": \"94104\",\n  \"country\": \"US\",\n  \"phone\": \"4151234567\",\n  \"email\": null,\n  \"residential\": false\n}\n```\n\nThis endpoint retrieves a specific address.\n\n### HTTP Request\n\n`POST https://www.vanlo.com/api/v1/addresses`\n\n### Create Address Request Parameters\n\nParameter | Type | Specification\n--------- | ----- | -----\nname | string | Name of the person or contact\ncompany | string | Company name associated with the address\nstreet1 | string | First line of the street address\nstreet2 | string | Second line of the street address\ncity | string | City\nstate | string | State or province (2-letter code for US, CA, AU)\nzip | string | Zip or postal code\ncountry | string | Two-letter ISO 3166 country code\nphone | string | Phone number\nemail | string | Email address\nresidential | boolean | Whether the address is residential\nverify | array | Set to `[\"delivery\"]` to verify the address on creation (best-effort - the address is still saved if it cannot be verified, with any problems returned in `verifications`). Sent at the top level of the request, alongside the `address` object\nverify_strict | array | Set to `[\"delivery\"]` to require a clean verification. If the address cannot be verified, the request is rejected with `422 CARRIER_REJECTED_ADDRESS` and the address is not saved. Sent at the top level of the request, alongside the `address` object\n\n## Get a Specific Address\n\n```shell\ncurl -X GET https://www.vanlo.com/api/v1/addresses/adr_... \\\n  -H 'Authorization: Bearer VANLO_API_KEY' \\\n```\n\n```ruby\nrequire 'vanlo'\nVanlo.api_key = 'VANLO_API_KEY'\n\nVanlo::Address.retrieve('adr_...')\n```\n\n```python\nimport vanlo\nvanlo.api_key = 'VANLO_API_KEY'\n\nvanlo.Address.retrieve('adr_...')\n```\n\n```php\nrequire_once(\"/path/to/lib/vanlo.php\");\n\\Vanlo\\Vanlo::setApiKey('VANLO_API_KEY');\n\n\\Vanlo\\Address::retrieve('adr_...');\n```\n\n```csharp\nusing Vanlo;\nClientManager.SetCurrent(\"VANLO_API_KEY\");\n\nAddress address = Address.Retrieve(\"adr_...\");\n```\n\n> The above command returns JSON structured like this:\n\n```json\n{\n  \"id\": \"adr_...\",\n  \"object\": \"Address\",\n  \"created_at\": \"2019-09-06T12:01:52.503Z\",\n  \"updated_at\": \"2019-09-06T12:01:52.503Z\",\n  \"name\": null,\n  \"company\": \"VANLO\",\n  \"street1\": \"123 MONTGOMERY ST\",\n  \"street2\": \"STE 400\",\n  \"city\": \"SAN FRANCISCO\",\n  \"state\": \"CA\",\n  \"zip\": \"94104\",\n  \"country\": \"US\",\n  \"phone\": \"4151234567\",\n  \"email\": null,\n  \"residential\": false\n}\n```\n\nThis endpoint retrieves a specific address.\n\n### HTTP Request\n\n`GET https://www.vanlo.com/api/v1/addresses/:id`\n\n### Address Errors\n\nBeyond the [common errors](/#errors) shared by every endpoint, address create and\nverification can return:\n\nStatus | Code | When it happens | How to handle\n------ | ---- | --------------- | -------------\n422 | `CARRIER_REJECTED_ADDRESS` | A strict verification (`verify_strict`) could not verify the address. | Fix the address using the problems in `error.errors[]` and retry.\n503 | `PROVIDER_503` | The address verification provider did not respond. | Retry; contact support if it persists.\n\nInvalid address fields return the common `BAD_REQUEST` 400 with the failing fields in\n`error.errors[]`. See [Errors](/#errors) for the full list and how to handle each one."
    },
    {
      "id": "parcels",
      "title": "Parcels",
      "content": "# Parcels\n\n## Parcel Object\n\nParameter | Type | Specification\n--------- | ----- | -----\nid | string | Unique identifier, begins with \"prcl_\"\nobject | string | \"Parcel\"\nlength | float | Length of the parcel in inches\nwidth | float | Width of the parcel in inches\nheight | float | Height of the parcel in inches\nweight | float | Weight of the parcel in ounces\npredefined_package | string | Carrier predefined package type, if applicable\ncreated_at | datetime | When the parcel was created\nupdated_at | datetime | When the parcel was last updated\n\n## Create a Parcel\n\n```shell\ncurl -X POST https://api.vanlo.com/api/v1/parcels \\\n  -H 'Authorization: Bearer VANLO_API_KEY' \\\n  -d 'parcel[length]=20.2' \\\n  -d 'parcel[width]=10.9' \\\n  -d 'parcel[height]=5' \\\n  -d 'parcel[weight]=65.9'\n```\n\n```ruby\nrequire 'vanlo'\nVanlo.api_key = 'VANLO_API_KEY'\n\nVanlo::Parcel.create(\n  length: 20.2,\n  width: 10.9,\n  height: 5,\n  weight: 65.9\n)\n```\n\n```python\nimport vanlo\nvanlo.api_key = 'VANLO_API_KEY'\n\nvanlo.Parcel.create(\n  length=20.2,\n  width=10.9,\n  height=5,\n  weight=65.9\n)\n```\n\n```php\nrequire_once(\"/path/to/lib/vanlo.php\");\n\\Vanlo\\Vanlo::setApiKey('VANLO_API_KEY');\n\n\\Vanlo\\Parcel::create(array(\n    \"length\" => 20.2,\n    \"width\" => 10.9,\n    \"height\" => 5,\n    \"weight\" => 65.9\n));\n```\n\n```csharp\nusing Vanlo;\nClientManager.SetCurrent(\"VANLO_API_KEY\");\n\nParcel parcel = Parcel.Create(new Dictionary<string, object>() {\n    { \"length\", 10 },\n    { \"width\", 20 },\n    { \"height\", 5 },\n    { \"weight\", 1.8 }\n});\n```\n\n> The above command returns JSON structured like this:\n\n```json\n{\n  \"id\": \"prcl_...\",\n  \"object\": \"Parcel\",\n  \"length\": 20.2,\n  \"width\": 10.9,\n  \"height\": 5.0,\n  \"predefined_package\": null,\n  \"weight\": 65.9,\n  \"created_at\": \"2019-04-22T05:40:57Z\",\n  \"updated_at\": \"2019-04-22T05:40:57Z\"\n}\n```\n\nCreate commonly sized parcels and save the returned id for use in future shipments. Remember to use the correct `predefined_package` when shipping with carrier supplied packaging.\n\n### HTTP Request\n\n`POST https://www.vanlo.com/api/v1/parcels`\n\n### Create Parcel Request Parameters\n\nParameter | Type | Specification\n--------- | ----- | -----\nlength | float | Length of the parcel in inches\nwidth | float | Width of the parcel in inches\nheight | float | Height of the parcel in inches\nweight | float | Weight of the parcel in ounces\npredefined_package | string | Carrier predefined package type\n\n<div class=\"pkg-selector\">\n<div class=\"pkg-selector__header\">\n<span class=\"pkg-selector__label\">Predefined package types by carrier</span>\n<div class=\"pkg-selector__tabs\">\n<button class=\"pkg-selector__tab active\" data-carrier=\"usps\">USPS</button>\n<button class=\"pkg-selector__tab\" data-carrier=\"fedex\">FedEx</button>\n<button class=\"pkg-selector__tab\" data-carrier=\"ups\">UPS</button>\n</div>\n</div>\n<div class=\"pkg-selector__panel active\" data-carrier=\"usps\">\n<table>\n<thead><tr><th>Package Type</th><th>Description</th></tr></thead>\n<tbody>\n<tr><td>Card</td><td>Postcard or greeting card</td></tr>\n<tr><td>Letter</td><td>Standard letter envelope</td></tr>\n<tr><td>Flat</td><td>Large envelope or flat</td></tr>\n<tr><td>SoftPack</td><td>Padded or poly mailer</td></tr>\n<tr><td>Parcel</td><td>Standard parcel</td></tr>\n<tr><td>LargeParcel</td><td>Large parcel (over 1 cubic foot)</td></tr>\n<tr><td>IrregularParcel</td><td>Non-rectangular parcel</td></tr>\n<tr><td>FlatRateEnvelope</td><td>USPS Flat Rate Envelope</td></tr>\n<tr><td>FlatRateLegalEnvelope</td><td>USPS Flat Rate Legal Envelope</td></tr>\n<tr><td>FlatRatePaddedEnvelope</td><td>USPS Flat Rate Padded Envelope</td></tr>\n<tr><td>FlatRateGiftCardEnvelope</td><td>USPS Flat Rate Gift Card Envelope</td></tr>\n<tr><td>FlatRateWindowEnvelope</td><td>USPS Flat Rate Window Envelope</td></tr>\n<tr><td>FlatRateCardboardEnvelope</td><td>USPS Flat Rate Cardboard Envelope</td></tr>\n<tr><td>SmallFlatRateEnvelope</td><td>USPS Small Flat Rate Envelope</td></tr>\n<tr><td>SmallFlatRateBox</td><td>USPS Small Flat Rate Box</td></tr>\n<tr><td>MediumFlatRateBox</td><td>USPS Medium Flat Rate Box</td></tr>\n<tr><td>LargeFlatRateBox</td><td>USPS Large Flat Rate Box</td></tr>\n<tr><td>LargeFlatRateBoxAPOFPO</td><td>USPS Large Flat Rate Box (APO/FPO/DPO)</td></tr>\n<tr><td>RegionalRateBoxA</td><td>USPS Regional Rate Box A</td></tr>\n<tr><td>RegionalRateBoxB</td><td>USPS Regional Rate Box B</td></tr>\n</tbody>\n</table>\n</div>\n<div class=\"pkg-selector__panel\" data-carrier=\"fedex\">\n<table>\n<thead><tr><th>Package Type</th><th>Description</th></tr></thead>\n<tbody>\n<tr><td>FedExEnvelope</td><td>FedEx Envelope</td></tr>\n<tr><td>FedExBox</td><td>FedEx Box</td></tr>\n<tr><td>FedExPak</td><td>FedEx Pak</td></tr>\n<tr><td>FedExTube</td><td>FedEx Tube</td></tr>\n<tr><td>FedEx10kgBox</td><td>FedEx 10kg Box</td></tr>\n<tr><td>FedEx25kgBox</td><td>FedEx 25kg Box</td></tr>\n<tr><td>FedExSmallBox</td><td>FedEx Small Box</td></tr>\n<tr><td>FedExMediumBox</td><td>FedEx Medium Box</td></tr>\n<tr><td>FedExLargeBox</td><td>FedEx Large Box</td></tr>\n<tr><td>FedExExtraLargeBox</td><td>FedEx Extra Large Box</td></tr>\n</tbody>\n</table>\n</div>\n<div class=\"pkg-selector__panel\" data-carrier=\"ups\">\n<table>\n<thead><tr><th>Package Type</th><th>Description</th></tr></thead>\n<tbody>\n<tr><td>UPSLetter</td><td>UPS Letter</td></tr>\n<tr><td>Tube</td><td>UPS Tube</td></tr>\n<tr><td>Pak</td><td>UPS Pak</td></tr>\n<tr><td>UPSExpressBox</td><td>UPS Express Box</td></tr>\n<tr><td>SmallExpressBox</td><td>UPS Small Express Box</td></tr>\n<tr><td>MediumExpressBox</td><td>UPS Medium Express Box</td></tr>\n<tr><td>LargeExpressBox</td><td>UPS Large Express Box</td></tr>\n<tr><td>UPS10kgBox</td><td>UPS 10kg Box</td></tr>\n<tr><td>UPS25kgBox</td><td>UPS 25kg Box</td></tr>\n<tr><td>Pallet</td><td>Pallet</td></tr>\n<tr><td>BPMParcel</td><td>Bound Printed Matter Parcel</td></tr>\n<tr><td>BPMFlat</td><td>Bound Printed Matter Flat</td></tr>\n<tr><td>Flat</td><td>Flat</td></tr>\n</tbody>\n</table>\n</div>\n</div>\n\n## Retrieve a Parcel\n\n```shell\ncurl -X GET https://www.vanlo.com/api/v1/parcels/prcl_... \\\n  -H 'Authorization: Bearer VANLO_API_KEY' \\\n```\n\n```ruby\nrequire 'vanlo'\nVanlo.api_key = 'VANLO_API_KEY'\n\nVanlo::Parcel.retrieve(\"prcl_...\")\n```\n\n```python\nimport vanlo\nvanlo.api_key = 'VANLO_API_KEY'\n\nvanlo.Parcel.retrieve(\"prcl_...\")\n```\n\n```php\nrequire_once(\"/path/to/lib/vanlo.php\");\n\\Vanlo\\Vanlo::setApiKey('VANLO_API_KEY');\n\n\\Vanlo\\Parcel::retrieve(\"prcl_...\");\n```\n\n```csharp\nusing Vanlo;\nClientManager.SetCurrent(\"VANLO_API_KEY\");\n\nParcel parcel = Parcel.Retrieve(\"prcl_...\");\n```\n\n> The above command returns JSON structured like this:\n\n```json\n{\n  \"id\": \"prcl_...\",\n  \"object\": \"Parcel\",\n  \"length\": 20.2,\n  \"width\": 10.9,\n  \"height\": 5.0,\n  \"predefined_package\": null,\n  \"weight\": 65.9,\n  \"created_at\": \"2019-04-22T05:40:57Z\",\n  \"updated_at\": \"2019-04-22T05:40:57Z\"\n}\n```\n\nGet a Parcel by its id. In general you should not need to use this in your automated solution. A Parcel's id can be inlined into the creation call to other objects. This allows you to only create one Parcel for each package you will be using.\n\n### HTTP Request\n\n`GET https://vanlo.com/api/v1/parcels/:id`\n\n### Parcel Errors\n\nCreating a parcel returns only the [common errors](/#errors) shared by every endpoint -\nmainly parameter validation: `BAD_REQUEST` 400 when a value cannot be saved, and\n`INVALID_PARAMETERS` 422 or `PARAMETER.REQUIRED` 422 for malformed or missing fields. See\n[Errors](/#errors) for the full list and how to handle each one."
    },
    {
      "id": "customs_infos",
      "title": "CustomsInfos",
      "content": "# CustomsInfos\n\n<aside class=\"endpoint-guide-link\">\n  <svg width=\"20\" height=\"20\" viewBox=\"0 0 16.5 15.5\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\"><path d=\"M0.75 1.75C0.75 1.19772 1.19772 0.75 1.75 0.75H5.25035C6.04606 0.75 6.80918 1.0819 7.37184 1.67269C7.93449 2.26347 8.25059 3.06475 8.25059 3.90025V14.75C8.25059 14.1234 8.01352 13.6985 7.59153 13.2554C7.16954 12.8124 6.59719 12.5634 6.00041 12.5634H1.75C1.19771 12.5634 0.75 12.1157 0.75 11.5634V1.75Z\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/><path d=\"M15.7506 1.75C15.7506 1.19772 15.3029 0.75 14.7506 0.75H11.2502C10.4545 0.75 9.6914 1.0819 9.12875 1.67269C8.5661 2.26347 8.25 3.06475 8.25 3.90025V14.75C8.25 14.1234 8.48707 13.6985 8.90906 13.2554C9.33105 12.8124 9.90339 12.5634 10.5002 12.5634H14.7506C15.3029 12.5634 15.7506 12.1157 15.7506 11.5634V1.75Z\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/></svg>\n  <span>Shipping internationally? The <a href=\"#\" class=\"api-guides-btn\" data-guide=\"customs\">Customs Guide</a> walks through attaching customs info to a shipment end-to-end.</span>\n</aside>\n\n## CustomsInfo Object\n\nParameter | Type | Specification\n--------- | ----- | -----\nid | string | Unique identifier, begins with \"cstinfo_\"\nobject | string | \"CustomsInfo\"\ncontents_type | string | Type of item being shipped: \"documents\", \"gift\", \"merchandise\", \"returned_goods\", \"sample\", or \"other\"\ncontents_explanation | string | Human-readable description of content, required if contents_type is \"other\"\ncustoms_certify | boolean | Whether the customs form has been signed by the customs_signer\ncustoms_signer | string | Name of the person signing the customs form\neel_pfc | string | Electronic Export License or PFC number. For items valued under $2,500 use \"NOEEI 30.37(a)\"\nnon_delivery_option | string | Action to take if the package cannot be delivered: \"abandon\" or \"return\" (default)\nrestriction_comments | string | Additional comments for restricted shipments, required if restriction_type is \"other\"\nrestriction_type | string | Type of restriction: \"none\", \"other\", \"quarantine\", or \"sanitary_phytosanitary_inspection\"\ncustoms_items | [[\\<CustomsItem\\>](/#customsitem-object)...] | Array of CustomsItem objects describing the contents\ncreated_at | datetime | When the customs info was created\nupdated_at | datetime | When the customs info was last updated\n\n### CustomsItem Object\n\nParameter | Type | Specification\n--------- | ----- | -----\nid | string | Unique identifier, begins with \"cstitem_\"\nobject | string | \"CustomsItem\"\ndescription | string | Human-readable description of the item\nhs_tariff_number | string | Harmonized System tariff code for the item\norigin_country | string | Two-letter ISO 3166 country code of the item's origin\nquantity | integer | Number of items\nvalue | float | Total value of the items in USD\nweight | float | Total weight of the items in ounces\ncreated_at | datetime | When the customs item was created\nupdated_at | datetime | When the customs item was last updated\n\n## Create CustomsInfo\n\n```shell\ncurl -X POST https://www.vanlo.com/api/v1/customs_infos \\\n  -H 'Authorization: Bearer VANLO_API_KEY' \\\n  -d 'customs_info[customs_certify]=true' \\\n  -d 'customs_info[customs_signer]=Name of Signer' \\\n  -d 'customs_info[contents_type]=merchandise' \\\n  -d 'customs_info[contents_explanation]=' \\\n  -d 'customs_info[restriction_type]=none' \\\n  -d 'customs_info[eel_pfc]=NOEEI 30.37(a)' \\\n  -d 'customs_info[customs_items][0][description]=Button' \\\n  -d 'customs_info[customs_items][0][quantity]=2' \\\n  -d 'customs_info[customs_items][0][value]=23' \\\n  -d 'customs_info[customs_items][0][weight]=11' \\\n  -d 'customs_info[customs_items][0][hs_tariff_number]=112233'\n  ```\n\n```ruby\nrequire 'vanlo'\nVanlo.api_key = 'VANLO_API_KEY'\n\nVanlo::CustomsInfo.create(\n  customs_certify: true,\n  customs_signer: 'Name of Signer',\n  contents_type: 'merchandise',\n  restriction_type: 'none',\n  eel_pfc: 'NOEEI 30.37(a)',\n  customs_items: [\n    {\n      description: 'Button',\n      quantity: '2',\n      value: '23',\n      weight: '11',\n      hs_tariff_number:'11223',\n    }\n  ]\n)\n```\n\n```python\nimport vanlo\nvanlo.api_key = 'VANLO_API_KEY'\n\ncustoms_info = vanlo.CustomsInfo.create(\n    customs_certify=True,\n    customs_signer='Name of Signer',\n    contents_type='merchandise',\n    restriction_type='none',\n    eel_pfc='NOEEI 30.37(a)',\n    customs_items=[{\n        'description': 'Button',\n        'quantity': '2',\n        'value': '23',\n        'weight': '11',\n        'hs_tariff_number':'11223',\n    }]\n)\n\n```\n\n```php\nrequire_once('/path/to/lib/vanlo.php');\n\\Vanlo\\Vanlo::setApiKey('VANLO_API_KEY');\n\n$customs_info = \\Vanlo\\CustomsInfo::create(array(\n  'customs_certify' => true,\n  'customs_signer' => 'Name of Signer',\n  'contents_type' => 'merchandise',\n  'restriction_type' => 'none',\n  'eel_pfc' => 'NOEEI 30.37(a)',\n  'customs_items' => array(array(\n    'description' => 'Button',\n    'quantity' => 2,\n    'value' => 23,\n    'weight' => 11,\n    'hs_tariff_number' => '11223',\n  ))\n));\n```\n\n```csharp\nusing Vanlo;\nClientManager.SetCurrent(\"VANLO_API_KEY\");\n\nDictionary<string, object> item = new Dictionary<string, object>() {\n    { \"description\", \"TShirt\" },\n    { \"quantity\", 1 },\n    { \"weight\", 8 },\n    { \"origin_country\", \"US\" }\n};\n\nCustomsInfo info = CustomsInfo.Create(new Dictionary<string, object>() {\n    { \"customs_certify\", true },\n    { \"eel_pfc\", \"NOEEI 30.37(a)\" },\n    { \"customs_signer\", \"Steve Brule\" },\n    { \"contents_type\", \"merchandise\" },\n    { \"customs_items\", new List<Dictionary<string, object>>() { item } }\n});\n```\n\n> The above command returns JSON structured like this:\n\n```json\n{\n  \"id\": \"cstinfo_...\",\n  \"object\": \"CustomsInfo\",\n  \"contents_explanation\": null,\n  \"contents_type\": \"merchandise\",\n  \"customs_certify\": true,\n  \"customs_signer\": \"Name of Signer\",\n  \"eel_pfc\": \"NOEEI 30.37(a)\",\n  \"non_delivery_option\": \"return\",\n  \"restriction_comments\": null,\n  \"restriction_type\": \"none\",\n  \"customs_items\": [{\n      \"id\": \"cstitem_...\",\n      \"object\": \"CustomsItem\",\n      \"description\": \"T-Shirt\",\n      \"hs_tariff_number\": \"123456\",\n      \"origin_country\": \"US\",\n      \"quantity\": 1,\n      \"value\": 10,\n      \"weight\": 5,\n      \"created_at\": \"2019-04-22T07:17:51Z\",\n      \"updated_at\": \"2019-04-22T07:17:51Z\"\n    }, {\n      \"id\": \"cstitem_...\",\n      \"object\": \"CustomsItem\",\n      \"description\": \"Button\",\n      \"hs_tariff_number\": \"112233\",\n      \"origin_country\": \"US\",\n      \"quantity\": 2,\n      \"value\": 23,\n      \"weight\": 11,\n      \"created_at\": \"2019-04-22T07:17:51Z\",\n      \"updated_at\": \"2019-04-22T07:17:51Z\"\n    }\n  ],\n  \"created_at\": \"2019-04-22T07:17:51Z\",\n  \"updated_at\": \"2019-04-22T07:17:51Z\"\n}\n```\n\nA CustomsItem object describes goods for international shipment and should be created then included in a CustomsInfo object.\n\n### HTTP Request\n\n`POST https://www.vanlo.com/api/v1/customs_infos`\n\n### Create CustomsInfo Request Parameters\n\nParameter | Type | Specification\n--------- | ----- | -----\neel_pfc | string | Electronic Export License or PFC number. For items valued under $2,500 use \"NOEEI 30.37(a)\"\ncontents_type | string | \"documents\", \"gift\", \"merchandise\", \"returned_goods\", \"sample\", or \"other\"\ncontents_explanation | string | Description of goods, required when contents_type is \"other\"\ncustoms_certify | boolean | Whether the customs form has been signed\ncustoms_signer | string | Name of the person signing the customs form\nrestriction_type | string | \"none\", \"other\", \"quarantine\", or \"sanitary_phytosanitary_inspection\"\nrestriction_comments | string | Additional comments, required when restriction_type is \"other\"\nnon_delivery_option | string | \"abandon\" or \"return\" (default)\ncustoms_items | [[\\<CustomsItem\\>](/#customsitem-object)...] | Array of CustomsItem objects describing the contents\n\n### Customs Info Errors\n\nCreating customs info returns the [common errors](/#errors) shared by every endpoint. The\nnested `customs_items` array is validated too: a missing array returns `PARAMETER.REQUIRED`\n422, and invalid item fields return `INVALID_PARAMETERS` 422 or `BAD_REQUEST` 400. See\n[Errors](/#errors) for the full list and how to handle each one."
    },
    {
      "id": "batches",
      "title": "Batches",
      "content": "# Batches\n\n<aside class=\"endpoint-guide-link\">\n  <svg width=\"20\" height=\"20\" viewBox=\"0 0 16.5 15.5\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\"><path d=\"M0.75 1.75C0.75 1.19772 1.19772 0.75 1.75 0.75H5.25035C6.04606 0.75 6.80918 1.0819 7.37184 1.67269C7.93449 2.26347 8.25059 3.06475 8.25059 3.90025V14.75C8.25059 14.1234 8.01352 13.6985 7.59153 13.2554C7.16954 12.8124 6.59719 12.5634 6.00041 12.5634H1.75C1.19771 12.5634 0.75 12.1157 0.75 11.5634V1.75Z\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/><path d=\"M15.7506 1.75C15.7506 1.19772 15.3029 0.75 14.7506 0.75H11.2502C10.4545 0.75 9.6914 1.0819 9.12875 1.67269C8.5661 2.26347 8.25 3.06475 8.25 3.90025V14.75C8.25 14.1234 8.48707 13.6985 8.90906 13.2554C9.33105 12.8124 9.90339 12.5634 10.5002 12.5634H14.7506C15.3029 12.5634 15.7506 12.1157 15.7506 11.5634V1.75Z\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/></svg>\n  <span>Batching shipments for the first time? The <a href=\"#\" class=\"api-guides-btn\" data-guide=\"batch\">Batch Guide</a> covers creating, purchasing, and downloading labels for a batch.</span>\n</aside>\n\n## Batch Object\n\nParameter | Type | Specification\n--------- | ----- | -----\nid | string | Unique identifier, begins with \"batch_\"\nobject | string | \"Batch\"\nstate | string | Current state of the batch: \"creating\", \"created\", \"purchasing\", \"purchased\", \"purchase_failed\", \"label_generating\", or \"label_generated\"\nstatus | [\\<BatchStatus\\>](/#batchstatus-object) | Counts of shipments in each processing state\nnum_shipments | integer | Total number of shipments in the batch\nreference | string | Arbitrary reference string\nscan_form | [\\<ScanForm\\>](/#scan-forms) | The associated ScanForm object, if generated\nshipments | [[\\<BatchShipment\\>](/#batchshipment-object)...] | Array of BatchShipment objects with per-shipment status\nlabel_url | string | URL of the consolidated label PDF, if generated\ncreated_at | datetime | When the batch was created\nupdated_at | datetime | When the batch was last updated\n\n### BatchStatus Object\n\nParameter | Type | Specification\n--------- | ----- | -----\ncreated | integer | Number of shipments that have been created but not yet queued\nqueued_for_purchase | integer | Number of shipments queued for postage purchase\ncreation_failed | integer | Number of shipments that failed to be created\npostage_purchased | integer | Number of shipments with postage successfully purchased\npostage_purchase_failed | integer | Number of shipments where postage purchase failed\n\n### BatchShipment Object\n\nParameter | Type | Specification\n--------- | ----- | -----\nid | string | Unique identifier of the shipment, begins with \"shp_\"\nbatch_status | string | Status of the shipment within the batch: \"created\", \"queued_for_purchase\", \"postage_purchased\", or \"postage_purchase_failed\"\nbatch_message | string | Error message if the shipment failed during batch processing\ntracking_code | string | Tracking code for the shipment, if purchased\n\n## Create a Batch\n\n```shell\ncurl -X POST https://www.vanlo.com/api/v1/batches \\\n  -H 'Authorization: Bearer VANLO_API_KEY' \\\n  -d 'batch[shipments][0][id]=shp_...' \\\n  -d 'batch[shipments][1][id]=shp_...'\n```\n\n```ruby\nrequire 'vanlo'\nVanlo.api_key = 'VANLO_API_KEY'\n\nVanlo::Batch.create(shipments: [shipment])\n```\n\n```python\nimport vanlo\nvanlo.api_key = 'VANLO_API_KEY'\n\nvanlo.Batch.create(shipments = [shipment]);\n```\n\n```php\nrequire_once('/path/to/lib/vanlo.php');\n\\Vanlo\\Vanlo::setApiKey('VANLO_API_KEY');\n\n$batch = \\Vanlo\\Batch::create(array(\n  'shipments' => array(array(\n    'id' => 'shp_...'\n  ))\n));\n```\n\n```csharp\nusing Vanlo;\nClientManager.SetCurrent(\"VANLO_API_KEY\");\n\nBatch batch = Batch.Create(new Dictionary<string, object>() {\n    { \"shipments\", new List<Dictionary<string, object>>() {\n        new Dictionary<string, object>() { { \"id\", \"shp_...\" } }\n    } }\n});\n```\n\n> The above command returns JSON structured like this:\n\n```json\n{\n  \"id\": \"batch_...\",\n  \"object\": \"Batch\",\n  \"num_shipments\": 2,\n  \"reference\": null,\n  \"scan_form\": null,\n  \"shipments\": [\n    {\n      \"id\": \"shp_...\",\n      \"batch_status\": \"postage_purchased\",\n      \"batch_message\": null,\n      \"tracking_code\": \"9405500205903028777744\"\n    },\n    {\n      \"id\": \"shp_...\",\n      \"batch_status\": \"postage_purchased\",\n      \"batch_message\": null,\n      \"tracking_code\": \"9405500205903028777755\"\n    }\n  ],\n  \"state\": \"creating\",\n  \"status\": {\n    \"created\": 0,\n    \"queued_for_purchase\": 0,\n    \"creation_failed\": 0,\n    \"postage_purchased\": 2,\n    \"postage_purchase_failed\": 0\n  },\n  \"label_url\": null,\n  \"created_at\": \"2019-07-22T07:34:39Z\",\n  \"updated_at\": \"2019-07-22T07:34:39Z\"\n}\n```\n\nA Batch can be created with or without Shipments. When created with Shipments the initial state will be 'creating'. Once the state changes to created a webhook Event will be sent. When created with no Shipments the initial state will be 'created' and a webhook will be sent.\n\n### HTTP Request\n\n`POST https://www.vanlo.com/api/v1/batches`\n\n### Create Batch Parameters\n\nParameter | Type | Specification\n--------- | ----- | -----\nshipments | [[\\<Shipment\\>](/#shipments)...] | Array of Shipment objects (or objects with shipment id) to include in the batch\n\n## Buy a Batch\n\n```shell\ncurl -X POST https://www.vanlo.com/api/v1/batches/batch_.../buy \\\n  -H 'Authorization: Bearer VANLO_API_KEY'\n```\n\n```ruby\nrequire 'vanlo'\nVanlo.api_key = 'VANLO_API_KEY'\n\nbatch = Vanlo::Batch.retrieve('batch_...')\nbatch.buy\n```\n\n```python\nimport vanlo\nvanlo.api_key = 'VANLO_API_KEY'\n\nbatch = vanlo.Batch.retrieve('batch_...')\nbatch.buy()\n```\n\n```php\nrequire_once('/path/to/lib/vanlo.php');\n\\Vanlo\\Vanlo::setApiKey('VANLO_API_KEY');\n\n$batch = \\Vanlo\\Batch::retrieve('batch_...');\n$batch->buy();\n```\n\n```csharp\nusing Vanlo;\nClientManager.SetCurrent(\"VANLO_API_KEY\");\n\nBatch batch = Batch.Retrieve(\"batch_...\");\nbatch.Buy();\n```\n\n> The above command returns JSON structured like this:\n\n```json\n{\n  \"id\": \"batch_...\",\n  \"object\": \"Batch\",\n  \"num_shipments\": 2,\n  \"reference\": null,\n  \"scan_form\": null,\n  \"shipments\": [\n    {\n      \"id\": \"shp_...\",\n      \"batch_status\": \"queued_for_purchase\",\n      \"batch_message\": null,\n      \"tracking_code\": null\n    },\n    {\n      \"id\": \"shp_...\",\n      \"batch_status\": \"queued_for_purchase\",\n      \"batch_message\": null,\n      \"tracking_code\": null\n    }\n  ],\n  \"state\": \"purchasing\",\n  \"status\": {\n    \"created\": 0,\n    \"queued_for_purchase\": 2,\n    \"creation_failed\": 0,\n    \"postage_purchased\": 0,\n    \"postage_purchase_failed\": 0\n  },\n  \"label_url\": null,\n  \"created_at\": \"2019-07-22T07:34:39Z\",\n  \"updated_at\": \"2019-07-22T07:34:39Z\"\n}\n```\n\nPurchase all shipments in a Batch. This is an asynchronous operation - the Batch will immediately transition to the `purchasing` state and a webhook Event will be sent when all shipments have been processed. The Batch must be in the `created` state to be purchased.\n\n### HTTP Request\n\n`POST https://www.vanlo.com/api/v1/batches/:id/buy`\n\n### Buy Batch Parameters\n\nParameter | Type | Specification\n--------- | ----- | -----\nid | string | Unique identifier of the batch to purchase\n\n## Batch Labels\n\n```shell\ncurl -X POST https://www.vanlo.com/api/v1/batches/batch_.../label \\\n  -H 'Authorization: Bearer VANLO_API_KEY' \\\n  -d 'file_format=zpl'\n```\n\n```ruby\nrequire 'vanlo'\nVanlo.api_key = 'VANLO_API_KEY'\n\nbatch = Vanlo::Batch.retrieve('batch_...')\nbatch.label(file_format: 'zpl')\n```\n\n```python\nimport vanlo\nvanlo.api_key = 'VANLO_API_KEY'\n\nbatch = vanlo.Batch.retrieve('batch_...')\nbatch.label(file_format = 'zpl')\n```\n\n```php\nrequire_once('/path/to/lib/vanlo.php');\n\\Vanlo\\Vanlo::setApiKey('VANLO_API_KEY');\n\n$batch = \\Vanlo\\Batch::retrieve('batch_...');\n$batch->label(array('file_format' => 'zpl'));\n```\n\n```csharp\nusing Vanlo;\nClientManager.SetCurrent(\"VANLO_API_KEY\");\n\nBatch batch = Batch.Retrieve(\"batch_...\");\n\nbatch.GenerateLabel(\"zpl\");\n```\n\n> The above command returns JSON structured like this:\n\n```json\n{\n  \"id\": \"batch_...\",\n  \"object\": \"Batch\",\n  \"num_shipments\": 2,\n  \"reference\": null,\n  \"scan_form\": null,\n  \"shipments\": [\n    {\n      \"id\": \"shp_...\",\n      \"batch_status\": \"postage_purchased\",\n      \"batch_message\": null,\n      \"tracking_code\": \"9405500205903028777744\"\n    },\n    {\n      \"id\": \"shp_...\",\n      \"batch_status\": \"postage_purchased\",\n      \"batch_message\": null,\n      \"tracking_code\": \"9405500205903028777755\"\n    }\n  ],\n  \"state\": \"creating\",\n  \"status\": {\n    \"created\": 0,\n    \"queued_for_purchase\": 0,\n    \"creation_failed\": 0,\n    \"postage_purchased\": 2,\n    \"postage_purchase_failed\": 0\n  },\n  \"label_url\": null,\n  \"created_at\": \"2019-07-22T07:34:39Z\",\n  \"updated_at\": \"2019-07-22T07:34:39Z\"\n}\n```\n\nOne of the advantages of processing Shipments in batches is the ability to consolidate the PostageLabel into one file. This can only be done once for each batch and all Shipments must have a status of 'postage_purchased'.\n\nAvailable label formats are 'pdf', 'zpl' or 'epl2' format.\n\n### HTTP Request\n\n`POST https://www.vanlo.com/api/v1/batches/:id/label`\n\n### Batch Label Parameters\n\nParameter | Type | Specification\n--------- | ----- | -----\nfile_format | string | Label file format: \"pdf\", \"zpl\", or \"epl2\"\n\n## Scan Forms\n\n```shell\ncurl -X POST https://www.vanlo.com/api/v1/batches/batch_.../scan_form \\\n  -H 'Authorization: Bearer VANLO_API_KEY'\n```\n\n```ruby\nrequire 'vanlo'\nVanlo.api_key = 'VANLO_API_KEY'\n\nbatch = Vanlo::Batch.retrieve('batch_...')\nbatch.create_scan_form\n```\n\n```python\nimport vanlo\nvanlo.api_key = 'VANLO_API_KEY'\n\nbatch = vanlo.Batch.retrieve('batch_...')\nbatch.create_scan_form()\n```\n\n```php\nrequire_once('/path/to/lib/vanlo.php');\n\\Vanlo\\Vanlo::setApiKey('VANLO_API_KEY');\n\n$batch = \\Vanlo\\Batch::retrieve('batch_...');\n$batch->create_scan_form();\n```\n\n```csharp\nusing Vanlo;\nClientManager.SetCurrent(\"VANLO_API_KEY\");\n\nBatch batch = Batch.Retrieve(\"batch_...\");\n\nbatch.GenerateScanForm();\n```\n\n> The above command returns JSON structured like this:\n\n```json\n{\n  \"id\": \"batch_...\",\n  \"num_shipments\": 2,\n  \"object\": \"Batch\",\n  \"reference\": null,\n  \"scan_form\": null,\n  \"shipments\": [\n    {\n      \"id\": \"shp_...\",\n      \"batch_status\": \"created\",\n      \"batch_message\": null,\n      \"tracking_code\": null\n    },\n    {\n      \"id\": \"shp_...\",\n      \"batch_status\": \"created\",\n      \"batch_message\": null,\n      \"tracking_code\": null\n    }\n  ],\n  \"state\": \"creating\",\n  \"status\": {\n    \"created\": 2,\n    \"queued_for_purchase\": 0,\n    \"creation_failed\": 0,\n    \"postage_purchased\": 0,\n    \"postage_purchase_failed\": 0\n  },\n  \"label_url\": null,\n  \"created_at\": \"2019-07-22T07:34:39Z\",\n  \"updated_at\": \"2019-07-22T07:34:39Z\"\n}\n```\n\nSee Scan Form rules and Object Definition.\n\n### HTTP Request\n\n`POST https://www.vanlo.com/api/v1/batches/:id/scan_form`\n\n## Get a specific Batch\n\n```shell\ncurl -X GET https://www.vanlo.com/api/v1/batches/batch_... \\\n  -H 'Authorization: Bearer VANLO_API_KEY'\n```\n\n```ruby\nrequire 'vanlo'\nVanlo.api_key = 'VANLO_API_KEY'\n\nVanlo::Batch.retrieve('batch_...')\n```\n\n```python\nimport vanlo\nvanlo.api_key = 'VANLO_API_KEY'\n\nvanlo.Batch.retrieve('batch_...')\n```\n\n```php\nrequire_once('/path/to/lib/vanlo.php');\n\\Vanlo\\Vanlo::setApiKey('VANLO_API_KEY');\n\n$batch = \\Vanlo\\Batch::retrieve('batch_...');\n```\n\n```csharp\nusing Vanlo;\nClientManager.SetCurrent(\"VANLO_API_KEY\");\n\nBatch batch = Batch.Retrieve(\"batch_...\");\n```\n\n> The above command returns JSON structured like this:\n\n```json\n{\n  \"id\": \"batch_...\",\n  \"object\": \"Batch\",\n  \"num_shipments\": 2,\n  \"reference\": null,\n  \"scan_form\": null,\n  \"shipments\": [\n    {\n      \"id\": \"shp_...\",\n      \"batch_status\": \"created\",\n      \"batch_message\": null,\n      \"tracking_code\": null\n    },\n    {\n      \"id\": \"shp_...\",\n      \"batch_status\": \"created\",\n      \"batch_message\": null,\n      \"tracking_code\": null\n    }\n  ],\n  \"state\": \"creating\",\n  \"status\": {\n    \"created\": 2,\n    \"queued_for_purchase\": 0,\n    \"creation_failed\": 0,\n    \"postage_purchased\": 0,\n    \"postage_purchase_failed\": 0\n  },\n  \"label_url\": null,\n  \"created_at\": \"2019-07-22T07:34:39Z\",\n  \"updated_at\": \"2019-07-22T07:34:39Z\"\n}\n```\n\nThis endpoint retrieves a specific batch.\n\n### HTTP Request\n\n`GET https://www.vanlo.com/api/v1/batches/:id`\n\n### Batch Errors\n\nBatch actions return only the [common errors](/#errors), two of them with a batch-specific\ntrigger. Buying a batch that is already purchased, currently purchasing, or failed (or a batch\nthat could not be created) returns `UNPROCESSABLE_ENTITY` 422. A batch whose scan-form is already\nbeing generated returns `LOCKED` 429. The label and scan-form actions require a completed batch;\ncalling them earlier returns `BAD_REQUEST` 400. See [Errors](/#errors) for the full list and how\nto handle each one."
    },
    {
      "id": "scan_forms",
      "title": "ScanForms",
      "content": "# ScanForms\n\n## ScanForm Object\n\nParameter | Type | Specification\n--------- | ----- | -----\nid | string | Unique identifier, begins with \"sf_\"\nobject | string | \"ScanForm\"\ntracking_codes | [string...] | Array of tracking codes included in the scan form\naddress | [\\<Address\\>](/#addresses) | The Address that the scan form applies to\nstatus | string | Current status of the scan form: \"created\", \"ready\", or \"failed\"\nmessage | string | Error message if the scan form failed to generate\nform_url | string | URL of the scan form PDF document\nform | string | Base64-encoded scan form document, if available\nbatch_id | string | Identifier of the associated Batch, begins with \"batch_\"\nreference | string | Arbitrary reference string for the scan form\ncreated_at | datetime | When the scan form was created\nupdated_at | datetime | When the scan form was last updated\n\n## Create a ScanForm\n\n```shell\ncurl -X POST https://www.vanlo.com/api/v1/scan_forms \\\n  -H 'Authorization: Bearer VANLO_API_KEY' \\\n  -d 'shipments[0][id]=shp_...' \\\n  -d 'shipments[1][id]=shp_...'\n```\n\n```ruby\nrequire 'vanlo'\nVanlo.api_key = 'VANLO_API_KEY'\n\nshipment = Vanlo::Shipment.retrieve('shp_...')\n\nVanlo::ScanForm.create(shipments: [shipment])\n```\n\n```python\nimport vanlo\nvanlo.api_key = 'VANLO_API_KEY'\n\nvanlo.ScanForm.create(shipments=[shipment])\n```\n\n```php\nrequire_once('/path/to/lib/vanlo.php');\n\\Vanlo\\Vanlo::setApiKey('VANLO_API_KEY');\n\n$shipment = \\Vanlo\\Shipment::retrieve('shp_...')\n\n$scan_form = \\Vanlo\\ScanForm::create(array(\n  'shipments' => array($shipment)\n));\n```\n\n```csharp\nusing Vanlo;\nClientManager.SetCurrent(\"VANLO_API_KEY\");\n\nList<Shipment> shipments = new List<Shipment>() {\n    new Shipment() { id = \"shp_...\" }\n};\n\nScanForm scanForm = ScanForm.Create(shipments);\n```\n\n> The above command returns JSON structured like this:\n\n```json\n{\n   \"id\":\"sf_...\",\n   \"object\":\"ScanForm\",\n   \"created_at\":\"2019-01-20T22:59:03Z\",\n   \"updated_at\":\"2019-01-20T22:59:04Z\",\n   \"tracking_codes\":[\n      \"8888888888888888888888\"\n   ],\n   \"address\":{\n      \"id\":\"adr_...\",\n      \"object\":\"Address\",\n      \"created_at\":\"2019-10-04T19:08:20Z\",\n      \"updated_at\":\"2019-10-04T19:08:20Z\",\n      \"name\":\"Vanlo\",\n      \"company\":null,\n      \"street1\":\"123 MONTGOMERY ST\",\n      \"street2\":\"STE 400\",\n      \"city\":\"SAN FRANCISCO\",\n      \"state\":\"CA\",\n      \"zip\":\"94104\",\n      \"country\":\"US\",\n      \"phone\":\"4151234567\",\n      \"email\":\"from@example.com\",\n      \"residential\":null,\n      \"verifications\":{}\n   },\n   \"status\":\"created\",\n   \"message\":null,\n   \"form_url\":\"https://....pdf\",\n   \"form_file_type\":null,\n   \"batch_id\":\"batch_...\",\n   \"confirmation\":null\n}\n```\n\nA ScanForm can be created in two ways:\n\nAdd Shipments to a Batch and create a ScanForm for a Batch of Shipments or create a ScanForm for shipments directly without adding shipments to a Batch.\n\nNote: A Batch is created in the background for Shipments as an intermediate process to creating ScanForms. You can create a ScanForm for 1 or a group of Shipments.\n\n### HTTP Request\n\n`POST https://www.vanlo.com/api/v1/scan_forms`\n\n### Create ScanForm Request Parameters\n\nParameter | Type | Specification\n--------- | ----- | -----\nshipments | [[\\<Shipment\\>](/#shipments)...] | Array of Shipment objects (or objects with shipment id) to include in the scan form\n\n## Create ScanForm for All Eligible Shipments\n\n```shell\ncurl -X POST https://www.vanlo.com/api/v1/scan_forms/all \\\n  -H 'Authorization: Bearer VANLO_API_KEY'\n```\n\n```ruby\nrequire 'vanlo'\nVanlo.api_key = 'VANLO_API_KEY'\n\nVanlo::ScanForm.create_all\n```\n\n```python\nimport vanlo\nvanlo.api_key = 'VANLO_API_KEY'\n\nvanlo.ScanForm.create_all()\n```\n\n```php\nrequire_once('/path/to/lib/vanlo.php');\n\\Vanlo\\Vanlo::setApiKey('VANLO_API_KEY');\n\n$scan_form = \\Vanlo\\ScanForm::create_all();\n```\n\n```csharp\nusing Vanlo;\nClientManager.SetCurrent(\"VANLO_API_KEY\");\n\nScanForm scanForm = ScanForm.CreateAll();\n```\n\n> The above command returns JSON structured like this:\n\n```json\n{\n   \"id\":\"sf_...\",\n   \"object\":\"ScanForm\",\n   \"created_at\":\"2019-01-20T22:59:03Z\",\n   \"updated_at\":\"2019-01-20T22:59:04Z\",\n   \"tracking_codes\":[\n      \"8888888888888888888888\",\n      \"9999999999999999999999\"\n   ],\n   \"address\":{\n      \"id\":\"adr_...\",\n      \"object\":\"Address\",\n      \"created_at\":\"2019-10-04T19:08:20Z\",\n      \"updated_at\":\"2019-10-04T19:08:20Z\",\n      \"name\":\"Vanlo\",\n      \"company\":null,\n      \"street1\":\"123 MONTGOMERY ST\",\n      \"street2\":\"STE 400\",\n      \"city\":\"SAN FRANCISCO\",\n      \"state\":\"CA\",\n      \"zip\":\"94104\",\n      \"country\":\"US\",\n      \"phone\":\"4151234567\",\n      \"email\":\"from@example.com\",\n      \"residential\":null,\n      \"verifications\":{}\n   },\n   \"status\":\"created\",\n   \"message\":null,\n   \"form_url\":\"https://....pdf\",\n   \"batch_id\":\"batch_...\",\n}\n```\n\nCreates a ScanForm for all eligible purchased shipments without specifying shipment IDs individually. This is a convenience endpoint that is equivalent to calling `POST /scan_forms` but automatically includes all purchased shipments that have not yet been added to a scan form.\n\nFor FedEx shipments, pass the `carrier` parameter as `FedEx` along with an optional `carrier_account_id`.\n\n### HTTP Request\n\n`POST https://www.vanlo.com/api/v1/scan_forms/all`\n\n### Create ScanForm All Request Parameters\n\nParameter | Type | Specification\n--------- | ----- | -----\ncarrier | string | Carrier to create the scan form for (default: \"USPS\"). Set to \"FedEx\" for FedEx manifests\ncarrier_account_id | string | Carrier account id to use. Only required for FedEx when you have multiple FedEx accounts\n\n## Retrieve a list of a ScanForms\n\n```shell\ncurl -X GET https://www.vanlo.com/api/v1/scan_forms \\\n  -H 'Authorization: Bearer VANLO_API_KEY' \\\n  -d 'page_size=2'\n```\n\n```ruby\nrequire 'vanlo'\nVanlo.api_key = 'VANLO_API_KEY'\n\nVanlo::ScanForm.all(page_size: 2)\n```\n\n```python\nimport vanlo\nvanlo.api_key = 'VANLO_API_KEY'\n\nvanlo.ScanForm.all(page_size=2)\n```\n\n```php\nrequire_once('/path/to/lib/vanlo.php');\n\\Vanlo\\Vanlo::setApiKey('VANLO_API_KEY');\n\n$scan_forms = \\Vanlo\\ScanForm::all(array('page_size' => 2));\n```\n\n```csharp\nusing Vanlo;\nClientManager.SetCurrent(\"VANLO_API_KEY\");\n\nvar listParams = new Dictionary<string, object>() {\n    { \"page_size\", 2 },\n    { \"start_datetime\", \"2016-01-02T08:50:00Z\" }\n};\n\nScanFormList scanFormList = ScanForm.List(listParams);\n```\n\n> The above command returns JSON structured like this:\n\n```json\n{\n  \"scan_forms\":[\n    {\n      \"id\":\"sf_...\",\n      \"object\":\"ScanForm\",\n      \"created_at\":\"2019-01-20T23:06:56Z\",\n      \"updated_at\":\"2019-01-20T23:06:56Z\",\n      \"tracking_codes\":[\n        \"8888888888888888888888\"\n      ],\n      \"address\":{\n        \"id\":\"adr_...\",\n        \"object\":\"Address\",\n        \"created_at\":\"2019-10-04T19:08:20Z\",\n        \"updated_at\":\"2019-10-04T19:08:20Z\",\n        \"name\":\"Vanlo\",\n        \"company\":null,\n        \"street1\":\"123 MONTGOMERY ST\",\n        \"street2\":\"STE 400\",\n        \"city\":\"SAN FRANCISCO\",\n        \"state\":\"CA\",\n        \"zip\":\"94104\",\n        \"country\":\"US\",\n        \"phone\":\"4151234567\",\n        \"email\":\"from@example.com\",\n        \"residential\":null,\n        \"verifications\":{}\n      },\n      \"status\":\"created\",\n      \"message\":null,\n      \"form_url\":\"https://vanlo-files.s3-us-west-2.amazonaws.com/files/scan_form/20170120/f02edb1487474db2b7dddd36d467e1f1.pdf\",\n      \"batch_id\":\"batch_...\",\n    },\n    {\n      \"id\":\"sf_...\",\n      \"object\":\"ScanForm\",\n      \"created_at\":\"2019-01-20T23:06:48Z\",\n      \"updated_at\":\"2019-01-20T23:06:48Z\",\n      \"tracking_codes\":[],\n      \"address\":null,\n      \"status\":\"failed\",\n      \"message\":\"A consistent from_address is required to create a USPS ScanForm.\",\n      \"form_url\":null,\n      \"batch_id\":\"batch_...\",\n    }\n  ],\n  \"has_more\":true\n}\n```\n\nThe ScanForm List is a paginated list of all ScanForm objects associated with the given API key. It accepts a variety of parameters which can be used to modify the scope. The 'has_more' attribute indicates whether or not additional pages can be requested. The recommended way of paginating is to use either the 'before_id' or 'after_id' parameter to specify where the next page begins.\n\n### HTTP Request\n\n`GET https://www.vanlo.com/api/v1/scan_forms`\n\n### Retrieve a list of ScanForms Request Parameters\n\nParameter | Type | Specification\n--------- | ----- | -----\nbefore_id | string | Return scan forms created before this id\nafter_id | string | Return scan forms created after this id\nstart_datetime | datetime | Only return scan forms created after this timestamp\nend_datetime | datetime | Only return scan forms created before this timestamp\npage_size | integer | Number of scan forms to return per page (default 20)\n\n## Retrieve a ScanForm\n\n```shell\ncurl -X GET https://www.vanlo.com/api/v1/scan_forms/sf_... \\\n  -H 'Authorization: Bearer VANLO_API_KEY'\n```\n\n```ruby\nrequire 'vanlo'\nVanlo.api_key = 'VANLO_API_KEY'\n\nVanlo::ScanForm.retrieve('sf_...')\n```\n\n```python\nimport vanlo\nvanlo.api_key = 'VANLO_API_KEY'\n\nvanlo.ScanForm.retrieve('sf_...')\n```\n\n```php\nrequire_once('/path/to/lib/vanlo.php');\n\\Vanlo\\Vanlo::setApiKey('VANLO_API_KEY');\n\n$scan_form = \\Vanlo\\ScanForm::retrieve('sf_...')\n```\n\n```csharp\nusing Vanlo;\nClientManager.SetCurrent(\"VANLO_API_KEY\");\n\nScanForm otherScanForm = ScanForm.Retrieve(\"sf_...\");\n```\n\n> The above command returns JSON structured like this:\n\n```json\n{\n   \"id\":\"sf_...\",\n   \"object\":\"ScanForm\",\n   \"created_at\":\"2019-01-20T22:59:03Z\",\n   \"updated_at\":\"2019-01-20T22:59:04Z\",\n   \"tracking_codes\":[\n      \"8888888888888888888888\"\n   ],\n   \"address\":{\n      \"id\":\"adr_...\",\n      \"object\":\"Address\",\n      \"created_at\":\"2019-10-04T19:08:20Z\",\n      \"updated_at\":\"2019-10-04T19:08:20Z\",\n      \"name\":\"Vanlo\",\n      \"company\":null,\n      \"street1\":\"123 MONTGOMERY ST\",\n      \"street2\":\"STE 400\",\n      \"city\":\"SAN FRANCISCO\",\n      \"state\":\"CA\",\n      \"zip\":\"94104\",\n      \"country\":\"US\",\n      \"phone\":\"4151234567\",\n      \"email\":\"from@example.com\",\n      \"residential\":null,\n      \"verifications\":{}\n   },\n   \"status\":\"created\",\n   \"message\":null,\n   \"form_url\":\"https://....pdf\",\n   \"batch_id\":\"batch_...\",\n}\n```\n\nRetrieve a ScanForm by id.\n\n\n### HTTP Request\n\n`GET https://www.vanlo.com/api/v1/scan_forms/:id`\n\n### Retrieve a ScanForm Request Parameters\n\nParameter | Type | Specification\n--------- | ----- | -----\nid | string | Unique identifier of the scan form\n\n### Scan Form Errors\n\nScan-form creation returns only the [common errors](/#errors). The one with a scan-form-specific\ntrigger is `LOCKED` 429, returned when a scan-form is already being created for these shipments.\nScoping problems (no eligible shipments, mixed origin addresses, and similar) return\n`BAD_REQUEST` 400, with the affected ids in `error.details`. See [Errors](/#errors) for the\nfull list and how to handle each one."
    },
    {
      "id": "orders",
      "title": "Orders",
      "content": "# Orders\n\nAn Order groups multiple Shipments that share the same origin and destination addresses. This is useful when you need to ship several packages to the same recipient and want to compare rates across all of them at once.\n\nWhen an Order is created, rates are generated for each Shipment. The Order's `rates` field contains only carrier/service combinations that are available for ALL Shipments in the Order, with the rate amounts summed across all Shipments. This makes it easy to pick a single carrier and service for the entire Order.\n\n## Order Object\n\nParameter | Type | Specification\n--------- | ----- | -----\nid | string | Unique identifier, begins with \"order_\"\nobject | string | \"Order\"\nto_address | [\\<Address\\>](/#addresses) | The destination address shared by all shipments\nfrom_address | [\\<Address\\>](/#addresses) | The origin address shared by all shipments\nshipments | [[\\<Shipment\\>](/#shipments)...] | Array of Shipment objects in the order\nrates | [[\\<Rate\\>](/#rate-object)...] | Aggregated rates available across all shipments. Only includes carrier/service combinations available for every shipment. Rate amounts are summed across all shipments.\ncarrier | string | The carrier used to purchase the order, or null if not yet purchased\nservice | string | The service used to purchase the order, or null if not yet purchased\nmessages | array | Any carrier messages generated during rating\ncreated_at | datetime | When the order was created\nupdated_at | datetime | When the order was last updated\n\n## Create an Order\n\n```shell\ncurl -X POST https://www.vanlo.com/api/v1/orders \\\n  -H 'Authorization: Bearer VANLO_API_KEY' \\\n  -d 'order[to_address][name]=To Name' \\\n  -d 'order[to_address][street1]=To Street' \\\n  -d 'order[to_address][city]=To City' \\\n  -d 'order[to_address][state]=CA' \\\n  -d 'order[to_address][zip]=90277' \\\n  -d 'order[to_address][country]=US' \\\n  -d 'order[to_address][phone]=4151234567' \\\n  -d 'order[to_address][email]=to@example.com' \\\n  -d 'order[from_address][name]=From Name' \\\n  -d 'order[from_address][company]=From Company' \\\n  -d 'order[from_address][street1]=From Street 1' \\\n  -d 'order[from_address][city]=From City' \\\n  -d 'order[from_address][state]=CA' \\\n  -d 'order[from_address][zip]=94104' \\\n  -d 'order[from_address][country]=US' \\\n  -d 'order[from_address][phone]=4157654321' \\\n  -d 'order[from_address][email]=from@example.com' \\\n  -d 'order[shipments][0][parcel][length]=8' \\\n  -d 'order[shipments][0][parcel][width]=6' \\\n  -d 'order[shipments][0][parcel][height]=4' \\\n  -d 'order[shipments][0][parcel][weight]=20' \\\n  -d 'order[shipments][1][parcel][length]=12' \\\n  -d 'order[shipments][1][parcel][width]=10' \\\n  -d 'order[shipments][1][parcel][height]=5' \\\n  -d 'order[shipments][1][parcel][weight]=35'\n```\n\n```ruby\nrequire 'vanlo'\nVanlo.api_key = 'VANLO_API_KEY'\n\nVanlo::Order.create(\n  to_address: {\n    name: 'To Name',\n    street1: 'To Street',\n    city: 'To City',\n    state: 'CA',\n    zip: '90277',\n    country: 'US',\n    phone: '4151234567',\n    email: 'to@example.com'\n  },\n  from_address: {\n    name: 'From Name',\n    company: 'From Company',\n    street1: 'From Street 1',\n    city: 'From City',\n    state: 'CA',\n    zip: '94104',\n    country: 'US',\n    phone: '4157654321',\n    email: 'from@example.com'\n  },\n  shipments: [\n    {\n      parcel: { length: 8, width: 6, height: 4, weight: 20 }\n    },\n    {\n      parcel: { length: 12, width: 10, height: 5, weight: 35 }\n    }\n  ]\n)\n```\n\n```python\nimport vanlo\nvanlo.api_key = 'VANLO_API_KEY'\n\norder = vanlo.Order.create(\n  to_address={\n    \"name\": \"To Name\",\n    \"street1\": \"To Street\",\n    \"city\": \"To City\",\n    \"state\": \"CA\",\n    \"zip\": \"90277\",\n    \"country\": \"US\",\n    \"phone\": \"4151234567\",\n    \"email\": \"to@example.com\"\n  },\n  from_address={\n    \"name\": \"From Name\",\n    \"company\": \"From Company\",\n    \"street1\": \"From Street 1\",\n    \"city\": \"From City\",\n    \"state\": \"CA\",\n    \"zip\": \"94104\",\n    \"country\": \"US\",\n    \"phone\": \"4157654321\",\n    \"email\": \"from@example.com\"\n  },\n  shipments=[\n    {\n      \"parcel\": { \"length\": 8, \"width\": 6, \"height\": 4, \"weight\": 20 }\n    },\n    {\n      \"parcel\": { \"length\": 12, \"width\": 10, \"height\": 5, \"weight\": 35 }\n    }\n  ]\n)\n```\n\n```php\nrequire_once('/path/to/lib/vanlo.php');\n\\Vanlo\\Vanlo::setApiKey('VANLO_API_KEY');\n\n$order = \\Vanlo\\Order::create(array(\n  'to_address' => array(\n    'name' => 'To Name',\n    'street1' => 'To Street',\n    'city' => 'To City',\n    'state' => 'CA',\n    'zip' => '90277',\n    'country' => 'US',\n    'phone' => '4151234567',\n    'email' => 'to@example.com'\n  ),\n  'from_address' => array(\n    'name' => 'From Name',\n    'company' => 'From Company',\n    'street1' => 'From Street 1',\n    'city' => 'From City',\n    'state' => 'CA',\n    'zip' => '94104',\n    'country' => 'US',\n    'phone' => '4157654321',\n    'email' => 'from@example.com'\n  ),\n  'shipments' => array(\n    array(\n      'parcel' => array('length' => 8, 'width' => 6, 'height' => 4, 'weight' => 20)\n    ),\n    array(\n      'parcel' => array('length' => 12, 'width' => 10, 'height' => 5, 'weight' => 35)\n    )\n  )\n));\n```\n\n```csharp\nusing Vanlo;\nClientManager.SetCurrent(\"VANLO_API_KEY\");\n\nOrder order = Order.Create(new Dictionary<string, object>() {\n    { \"to_address\", new Dictionary<string, object>() {\n        { \"name\", \"To Name\" },\n        { \"street1\", \"To Street\" },\n        { \"city\", \"To City\" },\n        { \"state\", \"CA\" },\n        { \"zip\", \"90277\" },\n        { \"country\", \"US\" },\n        { \"phone\", \"4151234567\" },\n        { \"email\", \"to@example.com\" }\n    } },\n    { \"from_address\", new Dictionary<string, object>() {\n        { \"name\", \"From Name\" },\n        { \"company\", \"From Company\" },\n        { \"street1\", \"From Street 1\" },\n        { \"city\", \"From City\" },\n        { \"state\", \"CA\" },\n        { \"zip\", \"94104\" },\n        { \"country\", \"US\" },\n        { \"phone\", \"4157654321\" },\n        { \"email\", \"from@example.com\" }\n    } },\n    { \"shipments\", new List<Dictionary<string, object>>() {\n        new Dictionary<string, object>() {\n            { \"parcel\", new Dictionary<string, object>() {\n                { \"length\", 8 }, { \"width\", 6 }, { \"height\", 4 }, { \"weight\", 20 }\n            } }\n        },\n        new Dictionary<string, object>() {\n            { \"parcel\", new Dictionary<string, object>() {\n                { \"length\", 12 }, { \"width\", 10 }, { \"height\", 5 }, { \"weight\", 35 }\n            } }\n        }\n    } }\n});\n```\n\n> The above command returns JSON structured like this:\n\n```json\n{\n  \"id\": \"order_...\",\n  \"object\": \"Order\",\n  \"to_address\": {\n    \"id\": \"adr_...\",\n    \"object\": \"Address\",\n    \"name\": \"To Name\",\n    \"company\": null,\n    \"street1\": \"To Street\",\n    \"street2\": null,\n    \"city\": \"To City\",\n    \"state\": \"CA\",\n    \"zip\": \"90277\",\n    \"country\": \"US\",\n    \"phone\": \"4151234567\",\n    \"residential\": null,\n    \"email\": \"to@example.com\",\n    \"created_at\": \"2025-03-13T12:00:00Z\",\n    \"updated_at\": \"2025-03-13T12:00:00Z\"\n  },\n  \"from_address\": {\n    \"id\": \"adr_...\",\n    \"object\": \"Address\",\n    \"name\": \"From Name\",\n    \"company\": \"From Company\",\n    \"street1\": \"From Street 1\",\n    \"street2\": null,\n    \"city\": \"From City\",\n    \"state\": \"CA\",\n    \"zip\": \"94104\",\n    \"country\": \"US\",\n    \"phone\": \"4157654321\",\n    \"residential\": null,\n    \"email\": \"from@example.com\",\n    \"created_at\": \"2025-03-13T12:00:00Z\",\n    \"updated_at\": \"2025-03-13T12:00:00Z\"\n  },\n  \"shipments\": [\n    {\n      \"id\": \"shp_...\",\n      \"object\": \"Shipment\",\n      \"parcel\": {\n        \"id\": \"prcl_...\",\n        \"object\": \"Parcel\",\n        \"length\": 8.0,\n        \"width\": 6.0,\n        \"height\": 4.0,\n        \"predefined_package\": null,\n        \"weight\": 20.0,\n        \"created_at\": \"2025-03-13T12:00:00Z\",\n        \"updated_at\": \"2025-03-13T12:00:00Z\"\n      },\n      \"rates\": [\n        {\n          \"id\": \"rate_...\",\n          \"object\": \"Rate\",\n          \"carrier\": \"USPS\",\n          \"service\": \"Priority\",\n          \"rate\": \"7.91\",\n          \"delivery_date\": null,\n          \"delivery_date_guaranteed\": false,\n          \"delivery_days\": 2,\n          \"created_at\": \"2025-03-13T12:00:00Z\"\n        },\n        {\n          \"id\": \"rate_...\",\n          \"object\": \"Rate\",\n          \"carrier\": \"USPS\",\n          \"service\": \"Express\",\n          \"rate\": \"26.35\",\n          \"delivery_date\": null,\n          \"delivery_date_guaranteed\": false,\n          \"delivery_days\": null,\n          \"created_at\": \"2025-03-13T12:00:00Z\"\n        }\n      ],\n      \"created_at\": \"2025-03-13T12:00:00Z\",\n      \"updated_at\": \"2025-03-13T12:00:00Z\"\n    },\n    {\n      \"id\": \"shp_...\",\n      \"object\": \"Shipment\",\n      \"parcel\": {\n        \"id\": \"prcl_...\",\n        \"object\": \"Parcel\",\n        \"length\": 12.0,\n        \"width\": 10.0,\n        \"height\": 5.0,\n        \"predefined_package\": null,\n        \"weight\": 35.0,\n        \"created_at\": \"2025-03-13T12:00:00Z\",\n        \"updated_at\": \"2025-03-13T12:00:00Z\"\n      },\n      \"rates\": [\n        {\n          \"id\": \"rate_...\",\n          \"object\": \"Rate\",\n          \"carrier\": \"USPS\",\n          \"service\": \"Priority\",\n          \"rate\": \"11.50\",\n          \"delivery_date\": null,\n          \"delivery_date_guaranteed\": false,\n          \"delivery_days\": 2,\n          \"created_at\": \"2025-03-13T12:00:00Z\"\n        },\n        {\n          \"id\": \"rate_...\",\n          \"object\": \"Rate\",\n          \"carrier\": \"USPS\",\n          \"service\": \"Express\",\n          \"rate\": \"40.16\",\n          \"delivery_date\": null,\n          \"delivery_date_guaranteed\": false,\n          \"delivery_days\": null,\n          \"created_at\": \"2025-03-13T12:00:00Z\"\n        }\n      ],\n      \"created_at\": \"2025-03-13T12:00:00Z\",\n      \"updated_at\": \"2025-03-13T12:00:00Z\"\n    }\n  ],\n  \"rates\": [\n    {\n      \"id\": \"rate_...\",\n      \"object\": \"Rate\",\n      \"carrier\": \"USPS\",\n      \"service\": \"Priority\",\n      \"rate\": \"19.41\",\n      \"delivery_date\": null,\n      \"delivery_date_guaranteed\": false,\n      \"delivery_days\": 2,\n      \"created_at\": \"2025-03-13T12:00:00Z\"\n    },\n    {\n      \"id\": \"rate_...\",\n      \"object\": \"Rate\",\n      \"carrier\": \"USPS\",\n      \"service\": \"Express\",\n      \"rate\": \"66.51\",\n      \"delivery_date\": null,\n      \"delivery_date_guaranteed\": false,\n      \"delivery_days\": null,\n      \"created_at\": \"2025-03-13T12:00:00Z\"\n    }\n  ],\n  \"carrier\": null,\n  \"service\": null,\n  \"messages\": [],\n  \"created_at\": \"2025-03-13T12:00:00Z\",\n  \"updated_at\": \"2025-03-13T12:00:00Z\"\n}\n```\n\nAn Order allows you to create multiple Shipments that share the same origin and destination, then rate and buy them together. Each Shipment in the Order gets its own rates, and the Order aggregates these into combined rates that reflect the total cost across all Shipments for each carrier/service combination.\n\nOnly carrier/service combinations available for ALL Shipments appear in the Order's `rates` array. The rate amount on each aggregated rate is the sum of the individual shipment rates for that carrier and service.\n\nTo create and buy in a single request, include the `service` parameter. The Order will be created and all Shipments will be purchased with the specified service immediately.\n\n### HTTP Request\n\n`POST https://www.vanlo.com/api/v1/orders`\n\n### Create Order Parameters\n\nParameter | Type | Required? | Specification\n--------- | ----- | --------- | ---------\nto_address | [\\<Address\\>](/#addresses) | Required | Destination address shared by all shipments in the order\nfrom_address | [\\<Address\\>](/#addresses) | Required | Origin address shared by all shipments in the order\nshipments | array | Required | Array of 1 to 100 shipment objects (see below)\ncarrier_accounts | [string...] | Optional | Carrier account ids to get rates from. Default: USPS\nservice | string | Optional | If provided, triggers create-and-buy: all shipments are purchased immediately with this service\n\n### Shipment Parameters (within shipments array)\n\nParameter | Type | Required? | Specification\n--------- | ----- | --------- | ---------\nparcel | [\\<Parcel\\>](/#parcels) | Required | Package dimensions and weight\ncustoms_info | [\\<CustomsInfo\\>](/#customsinfos) | Optional | Customs information for international shipments\ncarrier_accounts | [string...] | Optional | Override the order-level carrier_accounts for this shipment\noptions | object | Optional | Shipment options (see [Options Object](/#options-object))\nreference | string | Optional | Arbitrary reference string for this shipment\nreturn_address | [\\<Address\\>](/#addresses) | Optional | Return address if different from the order's from_address\nbuyer_address | [\\<Address\\>](/#addresses) | Optional | Buyer address if different from the order's to_address\n\n## Retrieve an Order\n\n```shell\ncurl -X GET https://www.vanlo.com/api/v1/orders/order_... \\\n  -H 'Authorization: Bearer VANLO_API_KEY'\n```\n\n```ruby\nrequire 'vanlo'\nVanlo.api_key = 'VANLO_API_KEY'\n\nVanlo::Order.retrieve('order_...')\n```\n\n```python\nimport vanlo\nvanlo.api_key = 'VANLO_API_KEY'\n\nvanlo.Order.retrieve('order_...')\n```\n\n```php\nrequire_once('/path/to/lib/vanlo.php');\n\\Vanlo\\Vanlo::setApiKey('VANLO_API_KEY');\n\n$order = \\Vanlo\\Order::retrieve('order_...');\n```\n\n```csharp\nusing Vanlo;\nClientManager.SetCurrent(\"VANLO_API_KEY\");\n\nOrder order = Order.Retrieve(\"order_...\");\n```\n\n> The above command returns JSON structured like this:\n\n```json\n{\n  \"id\": \"order_...\",\n  \"object\": \"Order\",\n  \"to_address\": {\n    \"id\": \"adr_...\",\n    \"object\": \"Address\",\n    \"name\": \"To Name\",\n    \"company\": null,\n    \"street1\": \"To Street\",\n    \"street2\": null,\n    \"city\": \"To City\",\n    \"state\": \"CA\",\n    \"zip\": \"90277\",\n    \"country\": \"US\",\n    \"phone\": \"4151234567\",\n    \"residential\": null,\n    \"email\": \"to@example.com\",\n    \"created_at\": \"2025-03-13T12:00:00Z\",\n    \"updated_at\": \"2025-03-13T12:00:00Z\"\n  },\n  \"from_address\": {\n    \"id\": \"adr_...\",\n    \"object\": \"Address\",\n    \"name\": \"From Name\",\n    \"company\": \"From Company\",\n    \"street1\": \"From Street 1\",\n    \"street2\": null,\n    \"city\": \"From City\",\n    \"state\": \"CA\",\n    \"zip\": \"94104\",\n    \"country\": \"US\",\n    \"phone\": \"4157654321\",\n    \"residential\": null,\n    \"email\": \"from@example.com\",\n    \"created_at\": \"2025-03-13T12:00:00Z\",\n    \"updated_at\": \"2025-03-13T12:00:00Z\"\n  },\n  \"shipments\": [\n    {\n      \"id\": \"shp_...\",\n      \"object\": \"Shipment\",\n      \"parcel\": {\n        \"id\": \"prcl_...\",\n        \"object\": \"Parcel\",\n        \"length\": 8.0,\n        \"width\": 6.0,\n        \"height\": 4.0,\n        \"predefined_package\": null,\n        \"weight\": 20.0,\n        \"created_at\": \"2025-03-13T12:00:00Z\",\n        \"updated_at\": \"2025-03-13T12:00:00Z\"\n      },\n      \"rates\": [\n        {\n          \"id\": \"rate_...\",\n          \"object\": \"Rate\",\n          \"carrier\": \"USPS\",\n          \"service\": \"Priority\",\n          \"rate\": \"7.91\",\n          \"delivery_date\": null,\n          \"delivery_date_guaranteed\": false,\n          \"delivery_days\": 2,\n          \"created_at\": \"2025-03-13T12:00:00Z\"\n        }\n      ],\n      \"created_at\": \"2025-03-13T12:00:00Z\",\n      \"updated_at\": \"2025-03-13T12:00:00Z\"\n    },\n    {\n      \"id\": \"shp_...\",\n      \"object\": \"Shipment\",\n      \"parcel\": {\n        \"id\": \"prcl_...\",\n        \"object\": \"Parcel\",\n        \"length\": 12.0,\n        \"width\": 10.0,\n        \"height\": 5.0,\n        \"predefined_package\": null,\n        \"weight\": 35.0,\n        \"created_at\": \"2025-03-13T12:00:00Z\",\n        \"updated_at\": \"2025-03-13T12:00:00Z\"\n      },\n      \"rates\": [\n        {\n          \"id\": \"rate_...\",\n          \"object\": \"Rate\",\n          \"carrier\": \"USPS\",\n          \"service\": \"Priority\",\n          \"rate\": \"11.50\",\n          \"delivery_date\": null,\n          \"delivery_date_guaranteed\": false,\n          \"delivery_days\": 2,\n          \"created_at\": \"2025-03-13T12:00:00Z\"\n        }\n      ],\n      \"created_at\": \"2025-03-13T12:00:00Z\",\n      \"updated_at\": \"2025-03-13T12:00:00Z\"\n    }\n  ],\n  \"rates\": [\n    {\n      \"id\": \"rate_...\",\n      \"object\": \"Rate\",\n      \"carrier\": \"USPS\",\n      \"service\": \"Priority\",\n      \"rate\": \"19.41\",\n      \"delivery_date\": null,\n      \"delivery_date_guaranteed\": false,\n      \"delivery_days\": 2,\n      \"created_at\": \"2025-03-13T12:00:00Z\"\n    }\n  ],\n  \"carrier\": null,\n  \"service\": null,\n  \"messages\": [],\n  \"created_at\": \"2025-03-13T12:00:00Z\",\n  \"updated_at\": \"2025-03-13T12:00:00Z\"\n}\n```\n\nRetrieve an Order by its id.\n\n### HTTP Request\n\n`GET https://www.vanlo.com/api/v1/orders/:id`\n\n## Buy an Order\n\n```shell\ncurl -X POST https://www.vanlo.com/api/v1/orders/order_.../buy \\\n  -H 'Authorization: Bearer VANLO_API_KEY' \\\n  -d 'carrier=USPS' \\\n  -d 'service=Priority'\n```\n\n```ruby\nrequire 'vanlo'\nVanlo.api_key = 'VANLO_API_KEY'\n\norder = Vanlo::Order.retrieve('order_...')\norder.buy(carrier: 'USPS', service: 'Priority')\n```\n\n```python\nimport vanlo\nvanlo.api_key = 'VANLO_API_KEY'\n\norder = vanlo.Order.retrieve('order_...')\norder.buy(carrier='USPS', service='Priority')\n```\n\n```php\nrequire_once('/path/to/lib/vanlo.php');\n\\Vanlo\\Vanlo::setApiKey('VANLO_API_KEY');\n\n$order = \\Vanlo\\Order::retrieve('order_...');\n$order->buy(array(\n  'carrier' => 'USPS',\n  'service' => 'Priority'\n));\n```\n\n```csharp\nusing Vanlo;\nClientManager.SetCurrent(\"VANLO_API_KEY\");\n\nOrder order = Order.Retrieve(\"order_...\");\norder.Buy(\"USPS\", \"Priority\");\n```\n\n> The above command returns JSON structured like this:\n\n```json\n{\n  \"id\": \"order_...\",\n  \"object\": \"Order\",\n  \"to_address\": {\n    \"id\": \"adr_...\",\n    \"object\": \"Address\",\n    \"name\": \"To Name\",\n    \"company\": null,\n    \"street1\": \"To Street\",\n    \"street2\": null,\n    \"city\": \"To City\",\n    \"state\": \"CA\",\n    \"zip\": \"90277\",\n    \"country\": \"US\",\n    \"phone\": \"4151234567\",\n    \"residential\": null,\n    \"email\": \"to@example.com\",\n    \"created_at\": \"2025-03-13T12:00:00Z\",\n    \"updated_at\": \"2025-03-13T12:00:00Z\"\n  },\n  \"from_address\": {\n    \"id\": \"adr_...\",\n    \"object\": \"Address\",\n    \"name\": \"From Name\",\n    \"company\": \"From Company\",\n    \"street1\": \"From Street 1\",\n    \"street2\": null,\n    \"city\": \"From City\",\n    \"state\": \"CA\",\n    \"zip\": \"94104\",\n    \"country\": \"US\",\n    \"phone\": \"4157654321\",\n    \"residential\": null,\n    \"email\": \"from@example.com\",\n    \"created_at\": \"2025-03-13T12:00:00Z\",\n    \"updated_at\": \"2025-03-13T12:00:00Z\"\n  },\n  \"shipments\": [\n    {\n      \"id\": \"shp_...\",\n      \"object\": \"Shipment\",\n      \"parcel\": {\n        \"id\": \"prcl_...\",\n        \"object\": \"Parcel\",\n        \"length\": 8.0,\n        \"width\": 6.0,\n        \"height\": 4.0,\n        \"predefined_package\": null,\n        \"weight\": 20.0,\n        \"created_at\": \"2025-03-13T12:00:00Z\",\n        \"updated_at\": \"2025-03-13T12:00:00Z\"\n      },\n      \"selected_rate\": {\n        \"id\": \"rate_...\",\n        \"object\": \"Rate\",\n        \"carrier\": \"USPS\",\n        \"service\": \"Priority\",\n        \"rate\": \"7.91\",\n        \"delivery_date\": null,\n        \"delivery_date_guaranteed\": false,\n        \"delivery_days\": 2,\n        \"created_at\": \"2025-03-13T12:00:00Z\"\n      },\n      \"tracking_code\": \"9405500205903028777744\",\n      \"postage_label\": {\n        \"id\": \"pl_...\",\n        \"object\": \"PostageLabel\",\n        \"label_url\": \"https://....png\",\n        \"created_at\": \"2025-03-13T12:00:01Z\",\n        \"updated_at\": \"2025-03-13T12:00:01Z\"\n      },\n      \"created_at\": \"2025-03-13T12:00:00Z\",\n      \"updated_at\": \"2025-03-13T12:00:01Z\"\n    },\n    {\n      \"id\": \"shp_...\",\n      \"object\": \"Shipment\",\n      \"parcel\": {\n        \"id\": \"prcl_...\",\n        \"object\": \"Parcel\",\n        \"length\": 12.0,\n        \"width\": 10.0,\n        \"height\": 5.0,\n        \"predefined_package\": null,\n        \"weight\": 35.0,\n        \"created_at\": \"2025-03-13T12:00:00Z\",\n        \"updated_at\": \"2025-03-13T12:00:00Z\"\n      },\n      \"selected_rate\": {\n        \"id\": \"rate_...\",\n        \"object\": \"Rate\",\n        \"carrier\": \"USPS\",\n        \"service\": \"Priority\",\n        \"rate\": \"11.50\",\n        \"delivery_date\": null,\n        \"delivery_date_guaranteed\": false,\n        \"delivery_days\": 2,\n        \"created_at\": \"2025-03-13T12:00:00Z\"\n      },\n      \"tracking_code\": \"9405500205903028777755\",\n      \"postage_label\": {\n        \"id\": \"pl_...\",\n        \"object\": \"PostageLabel\",\n        \"label_url\": \"https://....png\",\n        \"created_at\": \"2025-03-13T12:00:01Z\",\n        \"updated_at\": \"2025-03-13T12:00:01Z\"\n      },\n      \"created_at\": \"2025-03-13T12:00:00Z\",\n      \"updated_at\": \"2025-03-13T12:00:01Z\"\n    }\n  ],\n  \"rates\": [\n    {\n      \"id\": \"rate_...\",\n      \"object\": \"Rate\",\n      \"carrier\": \"USPS\",\n      \"service\": \"Priority\",\n      \"rate\": \"19.41\",\n      \"delivery_date\": null,\n      \"delivery_date_guaranteed\": false,\n      \"delivery_days\": 2,\n      \"created_at\": \"2025-03-13T12:00:00Z\"\n    }\n  ],\n  \"carrier\": \"USPS\",\n  \"service\": \"Priority\",\n  \"messages\": [],\n  \"created_at\": \"2025-03-13T12:00:00Z\",\n  \"updated_at\": \"2025-03-13T12:00:01Z\"\n}\n```\n\nPurchase all Shipments in the Order with the specified carrier and service. Each Shipment will receive its own tracking code and postage label. The carrier/service combination must be one that appears in the Order's aggregated `rates` array.\n\n### HTTP Request\n\n`POST https://www.vanlo.com/api/v1/orders/:id/buy`\n\n### Buy Order Parameters\n\nParameter | Type | Required? | Specification\n--------- | ----- | --------- | ---------\ncarrier | string | Required | The carrier to purchase with (e.g. \"USPS\")\nservice | string | Required | The service to purchase with (e.g. \"Priority\")\n\n### Order Errors\n\nBeyond the [common errors](/#errors) shared by every endpoint, order create and buy can\nreturn the same endpoint-specific codes as shipments, because an order buys several shipments\nat once:\n\nStatus | Code | When it happens | How to handle\n------ | ---- | --------------- | -------------\n402 | `PAYMENT_REQUIRED` | Buying with a balance that is too low. | Add funds, then retry.\n424 | `EXTERNAL_SERVICE_ERROR` | A carrier rejected or failed one of the order's shipments - bad dimensions, a service mismatch, an address the carrier would not accept, an expired rate, or the carrier being down. | Read `error.message` for the carrier's reason. Fix the shipment if it is an input problem, otherwise retry with backoff.\n\nA concurrent buy of the same order returns the common `LOCKED` 429. When more than one shipment\nfails, the response uses the most severe HTTP status. See [Errors](/#errors) for the full list\nand how to handle each one."
    },
    {
      "id": "reports",
      "title": "Reports",
      "content": "# Reports\n\n<aside class=\"endpoint-guide-link\">\n  <svg width=\"20\" height=\"20\" viewBox=\"0 0 16.5 15.5\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\"><path d=\"M0.75 1.75C0.75 1.19772 1.19772 0.75 1.75 0.75H5.25035C6.04606 0.75 6.80918 1.0819 7.37184 1.67269C7.93449 2.26347 8.25059 3.06475 8.25059 3.90025V14.75C8.25059 14.1234 8.01352 13.6985 7.59153 13.2554C7.16954 12.8124 6.59719 12.5634 6.00041 12.5634H1.75C1.19771 12.5634 0.75 12.1157 0.75 11.5634V1.75Z\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/><path d=\"M15.7506 1.75C15.7506 1.19772 15.3029 0.75 14.7506 0.75H11.2502C10.4545 0.75 9.6914 1.0819 9.12875 1.67269C8.5661 2.26347 8.25 3.06475 8.25 3.90025V14.75C8.25 14.1234 8.48707 13.6985 8.90906 13.2554C9.33105 12.8124 9.90339 12.5634 10.5002 12.5634H14.7506C15.3029 12.5634 15.7506 12.1157 15.7506 11.5634V1.75Z\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/></svg>\n  <span>Need a walkthrough? The <a href=\"#\" class=\"api-guides-btn\" data-guide=\"reports\">Reports Guide</a> shows how to choose a report type, create it, poll for completion, and download the CSV.</span>\n</aside>\n\n## Report Object\n\nA Report contains a csv that is a report of a certain type of object created within a specified date range.\n\nThe Report Object's `url` field will be `null` until the report has finished generating. You can either poll the Report's status, or use the `send_email` parameter to be notified when the report is ready.\n\nAvailable report types:\n\nType | Object Name | ID Prefix\n---- | ----------- | ---------\nshipment | ShipmentReport | shprep\\_\npayment_log | PaymentLogReport | plrep\\_\nshipment_invoice | ShipmentInvoiceReport | shpinvrep\\_\nrefund | RefundReport | refrep\\_\ntracking | TrackingReport | trkrep\\_\ninvoice_item | InvoiceItemReport | report\\_\nbalance_snapshot | BalanceSnapshotReport | report\\_\nfedex_detail | FedExDetailReport | fdxrep\\_\n\nParameter | Type | Specification\n--------- | ----- | -----\nid | string | Unique identifier, begins with a type-specific prefix (e.g., \"shprep\\_\" for shipment reports)\nobject | string | The type-specific object name (e.g., \"ShipmentReport\")\ncreated_at | datetime | When the report was created\nupdated_at | datetime | When the report was last updated\nstart_date | date | The start date of the report's date range\nend_date | date | The end date of the report's date range\nstatus | string | Current status: \"new\", \"generating\", \"ready\", or \"error\"\nurl | string | URL to download the report CSV (zipped). Expires after 1 hour. Null while generating\nurl_expires_at | datetime | When the download URL will expire. Null while generating\n\n## Create a Report\n\n```shell\ncurl -X POST https://www.vanlo.com/api/v1/reports/shipment \\\n  -H 'Authorization: Bearer VANLO_API_KEY' \\\n  -d 'start_date=2024-01-01' \\\n  -d 'end_date=2024-01-31'\n```\n\n```ruby\nrequire 'vanlo'\nVanlo.api_key = 'VANLO_API_KEY'\n\nVanlo::Report.create(\n  type: 'shipment',\n  start_date: '2024-01-01',\n  end_date: '2024-01-31'\n)\n```\n\n```python\nimport vanlo\nvanlo.api_key = 'VANLO_API_KEY'\n\nvanlo.Report.create(\n    type='shipment',\n    start_date='2024-01-01',\n    end_date='2024-01-31'\n)\n```\n\n```php\nrequire_once('/path/to/lib/vanlo.php');\n\\Vanlo\\Vanlo::setApiKey('VANLO_API_KEY');\n\n$report = \\Vanlo\\Report::create(array(\n  'type' => 'shipment',\n  'start_date' => '2024-01-01',\n  'end_date' => '2024-01-31'\n));\n```\n\n```csharp\nusing Vanlo;\nClientManager.SetCurrent(\"VANLO_API_KEY\");\n\nvar parameters = new Dictionary<string, object>() {\n    { \"type\", \"shipment\" },\n    { \"start_date\", \"2024-01-01\" },\n    { \"end_date\", \"2024-01-31\" }\n};\n\nReport report = Report.Create(parameters);\n```\n\n> The above command returns JSON structured like this:\n\n```json\n{\n  \"id\": \"shprep_...\",\n  \"object\": \"ShipmentReport\",\n  \"created_at\": \"2024-02-01T12:00:00Z\",\n  \"updated_at\": \"2024-02-01T12:00:00Z\",\n  \"start_date\": \"2024-01-01\",\n  \"end_date\": \"2024-01-31\",\n  \"status\": \"new\",\n  \"url\": null,\n  \"url_expires_at\": null\n}\n```\n\nCreate a Report of the given type for the specified date range. The report is generated asynchronously - the initial response will have a status of \"new\" and a null url. You can poll the report to check when it is ready, or use the `send_email` parameter to receive an email notification when the report is complete.\n\nThe date range must not exceed 31 days.\n\n### HTTP Request\n\n`POST https://www.vanlo.com/api/v1/reports/:type`\n\n### Create Report Request Parameters\n\nParameter | Type | Specification\n--------- | ----- | -----\ntype | string | The type of report to generate. One of: shipment, payment_log, shipment_invoice, refund, tracking, invoice_item, balance_snapshot, fedex_detail\nstart_date | date | Start date for the report (inclusive). Defaults to today if omitted\nend_date | date | End date for the report (inclusive). Defaults to start_date + 31 days if omitted\nsend_email | boolean | If true, send an email when the report is ready. Defaults to false\nsend_email_address | string | Email address to send the report to. Defaults to the account email if omitted\n\n## Retrieve a list of Reports\n\n```shell\ncurl -X GET https://www.vanlo.com/api/v1/reports/shipment \\\n  -H 'Authorization: Bearer VANLO_API_KEY' \\\n  -d 'page_size=2'\n```\n\n```ruby\nrequire 'vanlo'\nVanlo.api_key = 'VANLO_API_KEY'\n\nVanlo::Report.all(type: 'shipment', page_size: 2)\n```\n\n```python\nimport vanlo\nvanlo.api_key = 'VANLO_API_KEY'\n\nvanlo.Report.all(type='shipment', page_size=2)\n```\n\n```php\nrequire_once('/path/to/lib/vanlo.php');\n\\Vanlo\\Vanlo::setApiKey('VANLO_API_KEY');\n\n$reports = \\Vanlo\\Report::all(array(\n  'type' => 'shipment',\n  'page_size' => 2\n));\n```\n\n```csharp\nusing Vanlo;\nClientManager.SetCurrent(\"VANLO_API_KEY\");\n\nvar listParams = new Dictionary<string, object>() {\n    { \"type\", \"shipment\" },\n    { \"page_size\", 2 },\n    { \"start_datetime\", \"2024-01-01T00:00:00Z\" }\n};\n\nReportList reportList = Report.List(listParams);\n```\n\n> The above command returns JSON structured like this:\n\n```json\n{\n  \"reports\": [\n    {\n      \"id\": \"shprep_...\",\n      \"object\": \"ShipmentReport\",\n      \"created_at\": \"2024-02-01T12:00:00Z\",\n      \"updated_at\": \"2024-02-01T12:05:00Z\",\n      \"start_date\": \"2024-01-01\",\n      \"end_date\": \"2024-01-31\",\n      \"status\": \"ready\",\n      \"url\": \"https://vanlo-files.s3.amazonaws.com/files/reports/shipments-2024-01-01-2024-01-31.zip\",\n      \"url_expires_at\": \"2024-02-01T13:05:00Z\"\n    },\n    {\n      \"id\": \"shprep_...\",\n      \"object\": \"ShipmentReport\",\n      \"created_at\": \"2024-01-15T08:30:00Z\",\n      \"updated_at\": \"2024-01-15T08:35:00Z\",\n      \"start_date\": \"2023-12-15\",\n      \"end_date\": \"2024-01-14\",\n      \"status\": \"ready\",\n      \"url\": \"https://vanlo-files.s3.amazonaws.com/files/reports/shipments-2023-12-15-2024-01-14.zip\",\n      \"url_expires_at\": \"2024-02-01T13:05:00Z\"\n    }\n  ],\n  \"has_more\": true\n}\n```\n\nThe Report List is a paginated list of all Report objects of the given type associated with the given API key. It accepts a variety of parameters which can be used to modify the scope. The `has_more` attribute indicates whether or not additional pages can be requested. The recommended way of paginating is to use either the `before_id` or `after_id` parameter to specify where the next page begins.\n\n### HTTP Request\n\n`GET https://www.vanlo.com/api/v1/reports/:type`\n\n### Retrieve a list of Reports Request Parameters\n\nParameter | Type | Specification\n--------- | ----- | -----\ntype | string | The type of report to list. One of: shipment, payment_log, shipment_invoice, refund, tracking, invoice_item, balance_snapshot, fedex_detail\nbefore_id | string | Return reports created before this id\nafter_id | string | Return reports created after this id\nstart_datetime | datetime | Only return reports created after this timestamp\nend_datetime | datetime | Only return reports created before this timestamp\npage_size | integer | Number of reports to return per page (default 20)\n\n## Retrieve a Report\n\n```shell\ncurl -X GET https://www.vanlo.com/api/v1/reports/shprep_... \\\n  -H 'Authorization: Bearer VANLO_API_KEY'\n```\n\n```ruby\nrequire 'vanlo'\nVanlo.api_key = 'VANLO_API_KEY'\n\nVanlo::Report.retrieve('shprep_...')\n```\n\n```python\nimport vanlo\nvanlo.api_key = 'VANLO_API_KEY'\n\nvanlo.Report.retrieve('shprep_...')\n```\n\n```php\nrequire_once('/path/to/lib/vanlo.php');\n\\Vanlo\\Vanlo::setApiKey('VANLO_API_KEY');\n\n$report = \\Vanlo\\Report::retrieve('shprep_...');\n```\n\n```csharp\nusing Vanlo;\nClientManager.SetCurrent(\"VANLO_API_KEY\");\n\nReport report = Report.Retrieve(\"shprep_...\");\n```\n\n> The above command returns JSON structured like this:\n\n```json\n{\n  \"id\": \"shprep_...\",\n  \"object\": \"ShipmentReport\",\n  \"created_at\": \"2024-02-01T12:00:00Z\",\n  \"updated_at\": \"2024-02-01T12:05:00Z\",\n  \"start_date\": \"2024-01-01\",\n  \"end_date\": \"2024-01-31\",\n  \"status\": \"ready\",\n  \"url\": \"https://vanlo-files.s3.amazonaws.com/files/reports/shipments-2024-01-01-2024-01-31.zip\",\n  \"url_expires_at\": \"2024-02-01T13:05:00Z\"\n}\n```\n\nRetrieve a Report by id. The report can also be retrieved with the type prefix in the URL: `GET /api/v1/reports/shipment/shprep_...`\n\n### HTTP Request\n\n`GET https://www.vanlo.com/api/v1/reports/:id`\n\n### Retrieve a Report Request Parameters\n\nParameter | Type | Specification\n--------- | ----- | -----\nid | string | Unique identifier of the report\n\n### Report Errors\n\nReport endpoints return only the [common errors](/#errors). The one with a report-specific\ntrigger is `NOT_FOUND` 404, returned for an unknown report id or an unsupported report `type`.\nReport generation runs in the background, so a generation failure shows up in the report\nobject's `status` field, not as an HTTP error. See [Errors](/#errors) for the full list and\nhow to handle each one."
    },
    {
      "id": "carrier_accounts",
      "title": "Carrier Accounts",
      "content": "# Carrier Accounts\n\nA Carrier Account holds the credentials of a shipping account you hold directly with a carrier. Add one and Vanlo rates and buys labels on it using your own negotiated pricing, billed by the carrier to you rather than through your Vanlo balance.\n\nAccounts that Vanlo provides and bills you for are also returned by these endpoints, but they are managed by Vanlo: they cannot be updated or removed with your API key.\n\n<aside class=\"notice\">\nFedEx, UPS and UniUni accounts can be added right away. The other carriers are in a limited pilot and are enabled account by account: if a create call answers <code>limited pilot</code>, contact <a href=\"mailto:support@vanlo.com\">support@vanlo.com</a> to have that carrier turned on for you.\n</aside>\n\n## Carrier Account Object\n\nParameter | Type | Specification\n--------- | ----- | -----\nid | string | Unique identifier, begins with \"ca_\"\nobject | string | \"CarrierAccount\"\ntype | string | The account type, e.g. \"FedExAccount\" or \"PassportGlobalAccount\"\nreadable | string | The carrier's display name, e.g. \"FedEx\" or \"Passport\"\ndescription | string | Your own label for the account, or null\ncredentials | object | Always an empty object for accounts you added, and null for accounts managed by Vanlo. Credentials are write-only and are never returned\ncreated_at | datetime | When the account was added\nupdated_at | datetime | When the account was last changed\n\n## Supported Carrier Account Types\n\nThe `type` you send on create selects the carrier and decides which `credentials` fields are read. Any field not listed for a type is ignored.\n\nType | Carrier | Credentials\n--------- | ----- | -----\nFedExAccount | FedEx | mode (`test` or `prod`), client_id, client_secret, account_number\nUPSAccount | UPS | client_id, client_secret, account_number, mode (`test` or `prod`)\nUniUniAccount | UniUni | mode (`test` or `prod`), client_id, client_secret, customer_no\nDoorDashAccount | DoorDash | developer_id, key_id, signing_secret\nOnTracAccount | OnTrac | mode (`test` or `prod`), wsid, wskey, customer_branch\nVehoAccount | Veho | api_key, mode (`sandbox` or `prod`)\nPassportGlobalAccount | Passport | api_key, mode (`stg` or `prod`), company_name\nDhlEcsAccount | DHL eCommerce | client_id, client_secret, distribution_center, pickup_id\nAmazonShippingAccount | Amazon Shipping | None — authorized at the carrier, see below\n\n`FedexAccount` and `UpsAccount` are accepted as spellings of `FedExAccount` and `UPSAccount` on create; the response always carries the type as listed here.\n\nA Passport account you added before Vanlo integrated Passport directly keeps the `PassportGlobalAccount` type but was registered under the earlier credential set, so an update on it takes `api_key`, `company_name`, `email`, `name` and `phone` instead of the fields above. Both kinds carry the same `type` in the response; an `api_key` rotation works on either.\n\nWhat Vanlo checks before it creates the account differs by carrier: see [Create a Carrier Account](/#create-a-carrier-account).\n\n## Create a Carrier Account\n\n```shell\ncurl -X POST https://www.vanlo.com/api/v1/carrier_accounts \\\n  -H 'Authorization: Bearer VANLO_API_KEY' \\\n  -d 'carrier_account[type]=DhlEcsAccount' \\\n  -d 'carrier_account[description]=My DHL eCommerce' \\\n  -d 'carrier_account[credentials][client_id]=DHL_CLIENT_ID' \\\n  -d 'carrier_account[credentials][client_secret]=DHL_CLIENT_SECRET' \\\n  -d 'carrier_account[credentials][distribution_center]=USORD1' \\\n  -d 'carrier_account[credentials][pickup_id]=DHL_PICKUP_ID'\n```\n\n```ruby\nrequire 'vanlo'\nVanlo.api_key = 'VANLO_API_KEY'\n\nVanlo::CarrierAccount.create(\n  type: 'DhlEcsAccount',\n  description: 'My DHL eCommerce',\n  credentials: {\n    client_id: 'DHL_CLIENT_ID',\n    client_secret: 'DHL_CLIENT_SECRET',\n    distribution_center: 'USORD1',\n    pickup_id: 'DHL_PICKUP_ID'\n  }\n)\n```\n\n```python\nimport vanlo\nvanlo.api_key = 'VANLO_API_KEY'\n\nvanlo.CarrierAccount.create(\n    type='DhlEcsAccount',\n    description='My DHL eCommerce',\n    credentials={\n        'client_id': 'DHL_CLIENT_ID',\n        'client_secret': 'DHL_CLIENT_SECRET',\n        'distribution_center': 'USORD1',\n        'pickup_id': 'DHL_PICKUP_ID'\n    }\n)\n```\n\n```php\nrequire_once('/path/to/lib/vanlo.php');\n\\Vanlo\\Vanlo::setApiKey('VANLO_API_KEY');\n\n$carrier_account = \\Vanlo\\CarrierAccount::create(array(\n    'type' => 'DhlEcsAccount',\n    'description' => 'My DHL eCommerce',\n    'credentials' => array(\n        'client_id' => 'DHL_CLIENT_ID',\n        'client_secret' => 'DHL_CLIENT_SECRET',\n        'distribution_center' => 'USORD1',\n        'pickup_id' => 'DHL_PICKUP_ID'\n    )\n));\n```\n\n```csharp\nusing Vanlo;\nClientManager.SetCurrent(\"VANLO_API_KEY\");\n\nCarrierAccount carrierAccount = CarrierAccount.Create(\n    new Dictionary<string, object>() {\n        { \"type\", \"DhlEcsAccount\" },\n        { \"description\", \"My DHL eCommerce\" },\n        { \"credentials\", new Dictionary<string, object>() {\n            { \"client_id\", \"DHL_CLIENT_ID\" },\n            { \"client_secret\", \"DHL_CLIENT_SECRET\" },\n            { \"distribution_center\", \"USORD1\" },\n            { \"pickup_id\", \"DHL_PICKUP_ID\" }\n        }}\n    }\n);\n```\n\n> The above command returns JSON structured like this:\n\n```json\n{\n   \"id\":\"ca_...\",\n   \"object\":\"CarrierAccount\",\n   \"type\":\"DhlEcsAccount\",\n   \"readable\":\"DHLEcommerce\",\n   \"description\":\"My DHL eCommerce\",\n   \"credentials\":{},\n   \"created_at\":\"2026-08-09T12:00:00Z\",\n   \"updated_at\":\"2026-08-09T12:00:00Z\"\n}\n```\n\nSend the `type` of the account you are adding and the `credentials` that carrier issued you. What happens before the account is saved depends on the carrier:\n\n- FedEx, UniUni, DoorDash, OnTrac, Veho and Passport credentials are verified with the carrier before the account is created. A rejected set answers `422` and no account is created.\n- UPS credentials are not verified at create: the account is saved as sent, and a wrong credential surfaces the first time you rate or buy on it. Check the four fields before you send them.\n- DHL eCommerce and Amazon Shipping accounts are registered with the carrier before they are saved. That registration checks the shape of what you sent; a well-formed credential that is not actually valid at the carrier may only surface the first time you rate or buy on the account.\n\nThe new account is usable immediately: pass its `id` in the `carrier_accounts` parameter when you rate or create a shipment.\n\n### HTTP Request\n\n`POST https://www.vanlo.com/api/v1/carrier_accounts`\n\n### Create Carrier Account Request Parameters\n\nParameter | Type | Specification\n--------- | ----- | -----\ntype | string | Required. One of the types listed above\ncredentials | object | The credential fields for that type\ndescription | string | Optional. Your own label for the account\n\n### Carriers authorized at the carrier\n\n`AmazonShippingAccount` takes no credentials. The response carries an extra `oauth_url`, which the account holder must open to authorize Vanlo at the carrier. The account is created straight away but cannot rate or buy until that authorization is completed.\n\n> A create for an authorization-based carrier returns:\n\n```json\n{\n   \"id\":\"ca_...\",\n   \"object\":\"CarrierAccount\",\n   \"type\":\"AmazonShippingAccount\",\n   \"readable\":\"AmazonShipping\",\n   \"description\":null,\n   \"credentials\":{},\n   \"created_at\":\"2026-08-09T12:00:00Z\",\n   \"updated_at\":\"2026-08-09T12:00:00Z\",\n   \"oauth_url\":\"https://ship.amazon.com/authorize?...\"\n}\n```\n\n### Adding a FedEx account\n\n```shell\ncurl -X POST https://www.vanlo.com/api/v1/carrier_accounts \\\n  -H 'Authorization: Bearer VANLO_API_KEY' \\\n  -d 'carrier_account[type]=FedExAccount' \\\n  -d 'carrier_account[description]=My FedEx' \\\n  -d 'carrier_account[credentials][mode]=prod' \\\n  -d 'carrier_account[credentials][client_id]=FEDEX_CLIENT_ID' \\\n  -d 'carrier_account[credentials][client_secret]=FEDEX_CLIENT_SECRET' \\\n  -d 'carrier_account[credentials][account_number]=FEDEX_ACCOUNT_NUMBER'\n```\n\n```ruby\nrequire 'vanlo'\nVanlo.api_key = 'VANLO_API_KEY'\n\nVanlo::CarrierAccount.create(\n  type: 'FedExAccount',\n  description: 'My FedEx',\n  credentials: {\n    mode: 'prod',\n    client_id: 'FEDEX_CLIENT_ID',\n    client_secret: 'FEDEX_CLIENT_SECRET',\n    account_number: 'FEDEX_ACCOUNT_NUMBER'\n  }\n)\n```\n\n```python\nimport vanlo\nvanlo.api_key = 'VANLO_API_KEY'\n\nvanlo.CarrierAccount.create(\n    type='FedExAccount',\n    description='My FedEx',\n    credentials={\n        'mode': 'prod',\n        'client_id': 'FEDEX_CLIENT_ID',\n        'client_secret': 'FEDEX_CLIENT_SECRET',\n        'account_number': 'FEDEX_ACCOUNT_NUMBER'\n    }\n)\n```\n\n```php\nrequire_once('/path/to/lib/vanlo.php');\n\\Vanlo\\Vanlo::setApiKey('VANLO_API_KEY');\n\n$carrier_account = \\Vanlo\\CarrierAccount::create(array(\n    'type' => 'FedExAccount',\n    'description' => 'My FedEx',\n    'credentials' => array(\n        'mode' => 'prod',\n        'client_id' => 'FEDEX_CLIENT_ID',\n        'client_secret' => 'FEDEX_CLIENT_SECRET',\n        'account_number' => 'FEDEX_ACCOUNT_NUMBER'\n    )\n));\n```\n\n```csharp\nusing Vanlo;\nClientManager.SetCurrent(\"VANLO_API_KEY\");\n\nCarrierAccount carrierAccount = CarrierAccount.Create(\n    new Dictionary<string, object>() {\n        { \"type\", \"FedExAccount\" },\n        { \"description\", \"My FedEx\" },\n        { \"credentials\", new Dictionary<string, object>() {\n            { \"mode\", \"prod\" },\n            { \"client_id\", \"FEDEX_CLIENT_ID\" },\n            { \"client_secret\", \"FEDEX_CLIENT_SECRET\" },\n            { \"account_number\", \"FEDEX_ACCOUNT_NUMBER\" }\n        }}\n    }\n);\n```\n\n> The above command returns JSON structured like this:\n\n```json\n{\n   \"id\":\"ca_...\",\n   \"object\":\"CarrierAccount\",\n   \"type\":\"FedExAccount\",\n   \"readable\":\"FedEx\",\n   \"description\":\"My FedEx\",\n   \"credentials\":{},\n   \"created_at\":\"2026-08-19T12:00:00Z\",\n   \"updated_at\":\"2026-08-19T12:00:00Z\"\n}\n```\n\nA FedEx account takes the OAuth client id and secret of your FedEx developer project and the FedEx account number billed for the labels. `mode` is `test` for the FedEx sandbox and `prod` for real labels. The credentials are verified with FedEx before the account is created.\n\n## Retrieve a list of Carrier Accounts\n\n```shell\ncurl -X GET https://www.vanlo.com/api/v1/carrier_accounts \\\n  -H 'Authorization: Bearer VANLO_API_KEY'\n```\n\n```ruby\nrequire 'vanlo'\nVanlo.api_key = 'VANLO_API_KEY'\n\nVanlo::CarrierAccount.all\n```\n\n```python\nimport vanlo\nvanlo.api_key = 'VANLO_API_KEY'\n\ncarrier_accounts = vanlo.CarrierAccount.all()\n```\n\n```php\nrequire_once('/path/to/lib/vanlo.php');\n\\Vanlo\\Vanlo::setApiKey('VANLO_API_KEY');\n\n$carrier_accounts = \\Vanlo\\CarrierAccount::all();\n```\n\n```csharp\nusing Vanlo;\nClientManager.SetCurrent(\"VANLO_API_KEY\");\n\nList<CarrierAccount> carrierAccounts = CarrierAccount.List();\n```\n\n> The above command returns JSON structured like this:\n\n```json\n[\n   {\n      \"id\":\"ca_...\",\n      \"object\":\"CarrierAccount\",\n      \"type\":\"PassportGlobalAccount\",\n      \"readable\":\"Passport\",\n      \"description\":\"My Passport\",\n      \"credentials\":{},\n      \"created_at\":\"2026-08-09T12:00:00Z\",\n      \"updated_at\":\"2026-08-09T12:00:00Z\"\n   },\n   {\n      \"id\":\"ca_...\",\n      \"object\":\"CarrierAccount\",\n      \"type\":\"USPSAccount\",\n      \"readable\":\"USPS\",\n      \"description\":null,\n      \"credentials\":null,\n      \"created_at\":\"2026-01-04T09:30:00Z\",\n      \"updated_at\":\"2026-01-04T09:30:00Z\"\n   }\n]\n```\n\nRetrieve an unpaginated list of every Carrier Account available to the authenticated account, both the ones you added and the ones Vanlo manages for you. The response is a plain array.\n\nA `credentials` of `null` marks an account managed by Vanlo; an empty object marks one you added.\n\n### HTTP Request\n\n`GET https://www.vanlo.com/api/v1/carrier_accounts`\n\n## Retrieve a Carrier Account\n\n```shell\ncurl -X GET https://www.vanlo.com/api/v1/carrier_accounts/ca_... \\\n  -H 'Authorization: Bearer VANLO_API_KEY'\n```\n\n```ruby\nrequire 'vanlo'\nVanlo.api_key = 'VANLO_API_KEY'\n\nVanlo::CarrierAccount.retrieve('ca_...')\n```\n\n```python\nimport vanlo\nvanlo.api_key = 'VANLO_API_KEY'\n\nvanlo.CarrierAccount.retrieve('ca_...')\n```\n\n```php\nrequire_once('/path/to/lib/vanlo.php');\n\\Vanlo\\Vanlo::setApiKey('VANLO_API_KEY');\n\n$carrier_account = \\Vanlo\\CarrierAccount::retrieve(\"ca_...\");\n```\n\n```csharp\nusing Vanlo;\nClientManager.SetCurrent(\"VANLO_API_KEY\");\n\nCarrierAccount carrierAccount = CarrierAccount.Retrieve(\"ca_...\");\n```\n\n> The above command returns JSON structured like this:\n\n```json\n{\n   \"id\":\"ca_...\",\n   \"object\":\"CarrierAccount\",\n   \"type\":\"PassportGlobalAccount\",\n   \"readable\":\"Passport\",\n   \"description\":\"My Passport\",\n   \"credentials\":{},\n   \"created_at\":\"2026-08-09T12:00:00Z\",\n   \"updated_at\":\"2026-08-09T12:00:00Z\"\n}\n```\n\nRetrieve a Carrier Account by id. An id that is not on your account answers `404`.\n\n### HTTP Request\n\n`GET https://www.vanlo.com/api/v1/carrier_accounts/:id`\n\n### Retrieve Carrier Account Request Parameters\n\nParameter | Type | Specification\n--------- | ----- | -----\nid | string | Unique identifier of the carrier account\n\n## Update a Carrier Account\n\n```shell\ncurl -X PATCH https://www.vanlo.com/api/v1/carrier_accounts/ca_... \\\n  -H 'Authorization: Bearer VANLO_API_KEY' \\\n  -d 'carrier_account[description]=Passport (rotated)' \\\n  -d 'carrier_account[credentials][api_key]=NEW_PASSPORT_API_KEY'\n```\n\n```ruby\nrequire 'vanlo'\nVanlo.api_key = 'VANLO_API_KEY'\n\ncarrier_account = Vanlo::CarrierAccount.retrieve('ca_...')\ncarrier_account.update(\n  description: 'Passport (rotated)',\n  credentials: { api_key: 'NEW_PASSPORT_API_KEY' }\n)\n```\n\n```python\nimport vanlo\nvanlo.api_key = 'VANLO_API_KEY'\n\ncarrier_account = vanlo.CarrierAccount.retrieve('ca_...')\ncarrier_account.update(\n    description='Passport (rotated)',\n    credentials={'api_key': 'NEW_PASSPORT_API_KEY'}\n)\n```\n\n```php\nrequire_once('/path/to/lib/vanlo.php');\n\\Vanlo\\Vanlo::setApiKey('VANLO_API_KEY');\n\n$carrier_account = \\Vanlo\\CarrierAccount::retrieve(\"ca_...\");\n$carrier_account->update(array(\n    'description' => 'Passport (rotated)',\n    'credentials' => array('api_key' => 'NEW_PASSPORT_API_KEY')\n));\n```\n\n```csharp\nusing Vanlo;\nClientManager.SetCurrent(\"VANLO_API_KEY\");\n\nCarrierAccount carrierAccount = CarrierAccount.Retrieve(\"ca_...\");\ncarrierAccount.Update(\n    new Dictionary<string, object>() {\n        { \"description\", \"Passport (rotated)\" },\n        { \"credentials\", new Dictionary<string, object>() {\n            { \"api_key\", \"NEW_PASSPORT_API_KEY\" }\n        }}\n    }\n);\n```\n\n> The above command returns JSON structured like this:\n\n```json\n{\n   \"id\":\"ca_...\",\n   \"object\":\"CarrierAccount\",\n   \"type\":\"PassportGlobalAccount\",\n   \"readable\":\"Passport\",\n   \"description\":\"Passport (rotated)\",\n   \"credentials\":{},\n   \"created_at\":\"2026-08-09T12:00:00Z\",\n   \"updated_at\":\"2026-08-09T13:15:00Z\"\n}\n```\n\nRotate the credentials on an account you added, rename it, or both. Send only the fields you are changing: the credential fields you send replace the stored ones field by field, and omitting `credentials` leaves the stored ones untouched.\n\nEvery type under [Supported Carrier Account Types](/#supported-carrier-account-types) can be updated here. For FedEx, UniUni, DoorDash, OnTrac, Veho and Passport the new credentials are verified with the carrier first; a set the carrier rejects answers `422` and the stored credentials stay as they were. Once a label has been bought on a FedEx, UPS, UniUni, DoorDash, OnTrac, Veho or Passport account, its credentials can no longer be changed: the call answers `422`, and the way forward is to add a new account with the new credentials. Renaming is always allowed. DHL eCommerce and Amazon Shipping credentials, and those of a Passport account added before Vanlo integrated Passport directly, rotate at the carrier and are not affected by labels already bought. Accounts managed by Vanlo answer `403`.\n\n### HTTP Request\n\n`PATCH https://www.vanlo.com/api/v1/carrier_accounts/:id`\n\n### Update Carrier Account Request Parameters\n\nParameter | Type | Specification\n--------- | ----- | -----\nid | string | Unique identifier of the carrier account\ncredentials | object | Replacement credential fields for the account's type\ndescription | string | A new label for the account\n\n## Delete a Carrier Account\n\n```shell\ncurl -X DELETE https://www.vanlo.com/api/v1/carrier_accounts/ca_... \\\n  -H 'Authorization: Bearer VANLO_API_KEY'\n```\n\n```ruby\nrequire 'vanlo'\nVanlo.api_key = 'VANLO_API_KEY'\n\ncarrier_account = Vanlo::CarrierAccount.retrieve('ca_...')\ncarrier_account.delete\n```\n\n```python\nimport vanlo\nvanlo.api_key = 'VANLO_API_KEY'\n\ncarrier_account = vanlo.CarrierAccount.retrieve('ca_...')\ncarrier_account.delete()\n```\n\n```php\nrequire_once('/path/to/lib/vanlo.php');\n\\Vanlo\\Vanlo::setApiKey('VANLO_API_KEY');\n\n$carrier_account = \\Vanlo\\CarrierAccount::retrieve(\"ca_...\");\n$carrier_account->delete();\n```\n\n```csharp\nusing Vanlo;\nClientManager.SetCurrent(\"VANLO_API_KEY\");\n\nCarrierAccount carrierAccount = CarrierAccount.Retrieve(\"ca_...\");\ncarrierAccount.Destroy();\n```\n\n> The above command returns JSON structured like this:\n\n```json\n{}\n```\n\nRemove a Carrier Account you added. It stops being available for rating and buying, and stops appearing in the list.\n\nShipments already bought on the account are not affected: their labels, tracking and records stay exactly as they were.\n\nAccounts managed by Vanlo answer `403`.\n\n### HTTP Request\n\n`DELETE https://www.vanlo.com/api/v1/carrier_accounts/:id`\n\n### Delete Carrier Account Request Parameters\n\nParameter | Type | Specification\n--------- | ----- | -----\nid | string | Unique identifier of the carrier account\n\n### Carrier Account Errors\n\nBeyond the [common errors](/#errors) shared by every endpoint, the carrier account\nendpoints can return:\n\nStatus | Code | When it happens | How to handle\n------ | ---- | --------------- | -------------\n422 | `VALIDATION_ERROR` | The carrier rejected the credentials sent on create or update. | Check the fields listed for that `type` and retry.\n422 | `VALIDATION_ERROR` | The carrier is in a limited pilot and not yet enabled on your account. | Do not retry; contact support to have it turned on.\n422 | `UNPROCESSABLE_ENTITY` | The credentials of a FedEx, UPS, UniUni, DoorDash, OnTrac, Veho or Passport account cannot be changed any more: a label was already bought on it, or the account was set up for you by Vanlo support. | Add a new carrier account with the new credentials, or contact support; do not retry.\n\nAn account managed by Vanlo returns the common `FORBIDDEN` 403 on update and delete, an\nunknown id returns `NOT_FOUND` 404, and a `type` that is missing, blank or unsupported\nreturns `BAD_REQUEST` 400 - `PARAMETER.REQUIRED` 422 when the `carrier_account` parameter\nis absent altogether, and `PARAMETER.INVALID_TYPE` 422 when an update sends\n`credentials` as anything but an object (on create the same value answers `BAD_REQUEST` 400). See [Errors](/#errors) for the full list and how to handle each one."
    },
    {
      "id": "trackers",
      "title": "Trackers",
      "content": "# Trackers\n\n<aside class=\"endpoint-guide-link\">\n  <svg width=\"20\" height=\"20\" viewBox=\"0 0 16.5 15.5\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\"><path d=\"M0.75 1.75C0.75 1.19772 1.19772 0.75 1.75 0.75H5.25035C6.04606 0.75 6.80918 1.0819 7.37184 1.67269C7.93449 2.26347 8.25059 3.06475 8.25059 3.90025V14.75C8.25059 14.1234 8.01352 13.6985 7.59153 13.2554C7.16954 12.8124 6.59719 12.5634 6.00041 12.5634H1.75C1.19771 12.5634 0.75 12.1157 0.75 11.5634V1.75Z\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/><path d=\"M15.7506 1.75C15.7506 1.19772 15.3029 0.75 14.7506 0.75H11.2502C10.4545 0.75 9.6914 1.0819 9.12875 1.67269C8.5661 2.26347 8.25 3.06475 8.25 3.90025V14.75C8.25 14.1234 8.48707 13.6985 8.90906 13.2554C9.33105 12.8124 9.90339 12.5634 10.5002 12.5634H14.7506C15.3029 12.5634 15.7506 12.1157 15.7506 11.5634V1.75Z\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/></svg>\n  <span>Looking for a full example? Read the <a href=\"#\" class=\"api-guides-btn\" data-guide=\"tracking\">Tracking Guide</a> to see how shipments, tracking codes, and status updates fit together.</span>\n</aside>\n\n## Tracker Object\n\nParameter | Type | Specification\n--------- | ----- | -----\nid | string | Unique identifier, begins with \"trk_\"\nobject | string | \"Tracker\"\nshipment_id | string |\tThe id of the Shipment object associated with the Tracker (if any)\ntracking_details | [[\\<TrackingDetails\\>](/#trackingdetails-object)...] | Array of the associated TrackingDetail objects\ncarrier_detail | [[\\<CarrierDetail\\>](/#carrierdetail-object)...] | associated CarrierDetail object\ncarrier | string |The name of the carrier handling the shipment\nstatus | string | The current status of the package, possible values are \"unknown\", \"pre_transit\", \"in_transit\", \"out_for_delivery\", \"delivered\", \"available_for_pickup\", \"return_to_sender\", \"failure\", \"cancelled\" or \"error\"\ntracking_code | string | The tracking code provided by the carrier\npublic_url | string | URL to a publicly-accessible html page that shows tracking details for this tracker\ncreated_at | datetime |\t\nupdated_at | datetime |\t\n\n### CarrierDetail Object\n\nParameter | Type | Specification\n--------- | ----- | -----\nobject | string | \"CarrierDetail\"\nservice | string | The service level the associated shipment was shipped with (if available)\ncontainer_type | string | The type of container the associated shipment was shipped in (if available)\norigin_location | string | The location from which the package originated, stringified for presentation (if available)\nalternate_identifier | string | The alternate identifier for this package as provided by the carrier (if available)\ndestination_location | string | The location to which the package is being sent, stringified for presentation (if available)\nest_delivery_date_local | string | The estimated delivery date as provided by the carrier, in the local time zone (if available)\nest_delivery_time_local | string | The estimated delivery time as provided by the carrier, in the local time zone (if available)\nguaranteed_delivery_date | string | The date and time the carrier guarantees the package to be delivered by (if available)\ninitial_delivery_attempt | string | The date and time of the first attempt by the carrier to deliver the package (if available)\norigin_tracking_location | [\\<TrackingLocation\\>](/#trackinglocation-object) | The location from which the package originated\ndestination_tracking_location | [\\<TrackingLocation\\>](/#trackinglocation-object) | The location to which the package is being sent\n\n### TrackingDetails Object\n\nParameter | Type | Specification\n--------- | ----- | -----\nobject | string | \"TrackingDetail\"\nstatus | string | status of the package at the time of the scan event, possible values are \"unknown\", \"pre_transit\", \"in_transit\", \"out_for_delivery\", \"delivered\", \"available_for_pickup\", \"return_to_sender\", \"failure\", \"cancelled\" or \"error\"\nmessage | string | Description of the scan event\nsource | string | The original source of the information for this scan event, usually the carrier\ndatetime | datetime |\tThe timestamp when the tracking scan occurred\ntracking_location | [\\<TrackingLocation\\>](/#trackinglocation-object) | The location associated with the scan event\npod_urls | [string...] | Proof-of-delivery image URLs, if available\ncreated_at | datetime |\nupdated_at | datetime |\n\n### TrackingLocation Object\n\nParameter | Type | Specification\n--------- | ----- | -----\nzip | string | The postal code where the scan event occurred (if available)\ncity | string | The city where the scan event occurred (if available)\nstate | string | The state where the scan event occurred (if available)\ncountry | string | The country where the scan event occurred (if available)\n\n### Testing Specific Tracking States\n    \nSometimes you may want to simulate specific tracking statuses (e.g. \"out_for_delivery\") within your application to test how your application responds. Vanlo has a set of test tracking_codes that, when sent to the API, respond with specific tracking statuses and send a webhook Event to your test mode URL. The tracking updates that are sent by these tracking_codes will contain canned information, but it will be similar in form to the information normally provided by the carrier you selected.\n\n### Test Tracking Codes\ntracking_code | status\n------------- | -----\nEZ1000000001 |\tpre_transit\nEZ2000000002 |\tin_transit\nEZ3000000003 |\tout_for_delivery\nEZ4000000004 |\tdelivered\nEZ5000000005 |\treturn_to_sender\nEZ6000000006 |\tfailure\nEZ7000000007 |\tunknown\n\n## Create a Tracker\n\n```shell\ncurl -X POST https://api.vanlo.com/api/v1/trackers \\\n  -H 'Authorization: Bearer VANLO_API_KEY' \\\n  -d 'tracker[tracking_code]=EZ2000000002'\n```\n\n```ruby\nrequire 'vanlo'\nVanlo.api_key = 'VANLO_API_KEY'\n\nVanlo::Tracker.create(\n  tracking_code: 'EZ2000000002'\n)\n```\n\n```python\nimport vanlo\nvanlo.api_key = 'VANLO_API_KEY'\n\nvanlo.Tracker.create(tracking_code='EZ2000000002')\n```\n\n```php\nrequire_once('/path/to/lib/vanlo.php');\n\\Vanlo\\Vanlo::setApiKey('VANLO_API_KEY');\n\n\n$tracker = \\Vanlo\\Tracker::create(array(\n  'tracking_code' => 'EZ2000000002',\n));\n\n```\n\n```csharp\nusing Vanlo;\nClientManager.SetCurrent(\"VANLO_API_KEY\");\n\nTracker tracker = Tracker.Create(\"USPS\", \"EZ2000000002\");\n```\n\n> The above command returns JSON structured like this:\n\n```json\n{\n    \"id\": \"trk_...\",\n    \"object\": \"Tracker\",\n    \"shipment_id\": null,\n    \"tracking_details\": [\n        {\n            \"id\": null,\n            \"object\": \"TrackingDetail\",\n            \"created_at\": \"2020-03-31T16:09:53.467Z\",\n            \"updated_at\": \"2020-03-31T16:09:53.467Z\",\n            \"status\": \"pre_transit\",\n            \"message\": \"Pre-Shipment information received\",\n            \"datetime\": \"2020-02-29T16:09:53.000Z\",\n            \"source\": null,\n            \"tracking_location\": {\n                \"zip\": null,\n                \"city\": null,\n                \"state\": null,\n                \"country\": null\n            }\n        },\n        {\n            \"id\": null,\n            \"object\": \"TrackingDetail\",\n            \"created_at\": \"2020-03-31T16:09:53.487Z\",\n            \"updated_at\": \"2020-03-31T16:09:53.487Z\",\n            \"status\": \"pre_transit\",\n            \"message\": \"Shipping label created\",\n            \"datetime\": \"2020-03-01T10:58:53.000Z\",\n            \"source\": null,\n            \"tracking_location\": {\n                \"zip\": null,\n                \"city\": null,\n                \"state\": null,\n                \"country\": null\n            }\n        },\n        {\n            \"id\": null,\n            \"object\": \"TrackingDetail\",\n            \"created_at\": \"2020-03-31T16:09:53.500Z\",\n            \"updated_at\": \"2020-03-31T16:09:53.500Z\",\n            \"status\": \"in_transit\",\n            \"message\": \"Picked Up\",\n            \"datetime\": \"2020-03-01T16:09:53.000Z\",\n            \"source\": null,\n            \"tracking_location\": {\n                \"zip\": \"94612\",\n                \"city\": \"Oakland\",\n                \"state\": \"CA\",\n                \"country\": \"US\"\n            }\n        },\n        {\n            \"id\": null,\n            \"object\": \"TrackingDetail\",\n            \"created_at\": \"2020-03-31T16:09:53.516Z\",\n            \"updated_at\": \"2020-03-31T16:09:53.516Z\",\n            \"status\": \"in_transit\",\n            \"message\": \"Arrived at Sort Facility\",\n            \"datetime\": \"2020-03-02T10:36:53.000Z\",\n            \"source\": null,\n            \"tracking_location\": {\n                \"zip\": \"60290\",\n                \"city\": \"Chicago\",\n                \"state\": \"IL\",\n                \"country\": \"US\"\n            }\n        },\n        {\n            \"id\": null,\n            \"object\": \"TrackingDetail\",\n            \"created_at\": \"2020-03-31T16:09:53.530Z\",\n            \"updated_at\": \"2020-03-31T16:09:53.530Z\",\n            \"status\": \"in_transit\",\n            \"message\": \"Departed Sort Facility\",\n            \"datetime\": \"2020-03-03T11:16:53.000Z\",\n            \"source\": null,\n            \"tracking_location\": {\n                \"zip\": \"60290\",\n                \"city\": \"Chicago\",\n                \"state\": \"IL\",\n                \"country\": \"US\"\n            }\n        },\n        {\n            \"id\": null,\n            \"object\": \"TrackingDetail\",\n            \"created_at\": \"2020-03-31T16:09:53.544Z\",\n            \"updated_at\": \"2020-03-31T16:09:53.544Z\",\n            \"status\": \"in_transit\",\n            \"message\": \"Arrived at Distribution Center\",\n            \"datetime\": \"2020-03-03T16:09:53.000Z\",\n            \"source\": null,\n            \"tracking_location\": {\n                \"zip\": \"19087\",\n                \"city\": \"Radnor\",\n                \"state\": \"PA\",\n                \"country\": \"US\"\n            }\n        }\n    ],\n    \"created_at\": \"2020-03-31T16:09:53.436Z\",\n    \"updated_at\": \"2020-03-31T16:09:53.436Z\",\n    \"carrier\": \"USPS\",\n    \"status\": \"in_transit\",\n    \"tracking_code\": \"EZ...\",\n    \"public_url\": \"https://...\"\n}\n```\n\nA Tracker encapsulates all tracking information for a shipment.\n\n### HTTP Request\n\n`POST https://www.vanlo.com/api/v1/trackers`\n\n### Create Tracker Parameters\n\nParameter | Type | Specification\n--------- | ----- | -----\ntracking_code | string | The tracking code to track\ncarrier | string | Name of the carrier. Supported values: `USPS`, `FedEx`, `UPS`, `DHL`, `OLX`, `UniUni`, `OSM`, `OneParcel`, `P2PG`, `DoorDash`, `OnTrac`\n\n## List trackers\n\n```shell\ncurl -X GET https://api.vanlo.com/api/v1/trackers \\\n  -H 'Authorization: Bearer VANLO_API_KEY'\n```\n\n```ruby\nrequire 'vanlo'\nVanlo.api_key = 'VANLO_API_KEY'\n\nVanlo::Tracker.all\n```\n\n```python\nimport vanlo\nvanlo.api_key = 'VANLO_API_KEY'\n\nvanlo.Tracker.all(page_size = 2)\n```\n\n```php\nrequire_once('/path/to/lib/vanlo.php');\n\\Vanlo\\Vanlo::setApiKey('VANLO_API_KEY');\n\n$trackers = \\Vanlo\\Tracker::all(array(\n  'page_size' => 2\n));\n```\n\n```csharp\nusing Vanlo;\nClientManager.SetCurrent(\"VANLO_API_KEY\");\n\nlistParams = new Dictionary<string, object>() {\n    { \"page_size\", 2 },\n    { \"start_datetime\", \"2016-01-02T08:50:00Z\" }\n};\n\nTrackerList trackerList = Tracker.List(listParams);\nTrackerList nextTrackerList = trackerList.Next();\n```\n\n> The above command returns JSON structured like this:\n\n```json\n\n{\n    \"trackers\": [\n        {\n            \"id\": \"trk_...\",\n            \"object\": \"Tracker\",\n            \"shipment_id\": null,\n            \"tracking_details\": [\n                {\n                    \"id\": null,\n                    \"object\": \"TrackingDetail\",\n                    \"created_at\": \"2020-03-30T23:07:05.580Z\",\n                    \"updated_at\": \"2020-03-30T23:07:05.580Z\",\n                    \"status\": \"pre_transit\",\n                    \"message\": \"Pre-Shipment information received\",\n                    \"datetime\": \"2020-02-29T23:07:05.000Z\",\n                    \"source\": null,\n                    \"tracking_location\": {\n                        \"zip\": null,\n                        \"city\": null,\n                        \"state\": null,\n                        \"country\": null\n                    }\n                },\n                {\n                    \"id\": null,\n                    \"object\": \"TrackingDetail\",\n                    \"created_at\": \"2020-03-30T23:07:05.599Z\",\n                    \"updated_at\": \"2020-03-30T23:07:05.599Z\",\n                    \"status\": \"pre_transit\",\n                    \"message\": \"Shipping label created\",\n                    \"datetime\": \"2020-03-01T17:56:05.000Z\",\n                    \"source\": null,\n                    \"tracking_location\": {\n                        \"zip\": null,\n                        \"city\": null,\n                        \"state\": null,\n                        \"country\": null\n                    }\n                },\n                {\n                    \"id\": null,\n                    \"object\": \"TrackingDetail\",\n                    \"created_at\": \"2020-03-30T23:07:05.622Z\",\n                    \"updated_at\": \"2020-03-30T23:07:05.622Z\",\n                    \"status\": \"in_transit\",\n                    \"message\": \"Picked Up\",\n                    \"datetime\": \"2020-03-01T23:07:05.000Z\",\n                    \"source\": null,\n                    \"tracking_location\": {\n                        \"zip\": \"94612\",\n                        \"city\": \"Oakland\",\n                        \"state\": \"CA\",\n                        \"country\": \"US\"\n                    }\n                },\n                {\n                    \"id\": null,\n                    \"object\": \"TrackingDetail\",\n                    \"created_at\": \"2020-03-30T23:07:05.644Z\",\n                    \"updated_at\": \"2020-03-30T23:07:05.644Z\",\n                    \"status\": \"in_transit\",\n                    \"message\": \"Arrived at Sort Facility\",\n                    \"datetime\": \"2020-03-02T17:34:05.000Z\",\n                    \"source\": null,\n                    \"tracking_location\": {\n                        \"zip\": \"60290\",\n                        \"city\": \"Chicago\",\n                        \"state\": \"IL\",\n                        \"country\": \"US\"\n                    }\n                },\n                {\n                    \"id\": null,\n                    \"object\": \"TrackingDetail\",\n                    \"created_at\": \"2020-03-30T23:07:05.666Z\",\n                    \"updated_at\": \"2020-03-30T23:07:05.666Z\",\n                    \"status\": \"in_transit\",\n                    \"message\": \"Departed Sort Facility\",\n                    \"datetime\": \"2020-03-03T18:14:05.000Z\",\n                    \"source\": null,\n                    \"tracking_location\": {\n                        \"zip\": \"60290\",\n                        \"city\": \"Chicago\",\n                        \"state\": \"IL\",\n                        \"country\": \"US\"\n                    }\n                }\n            ],\n            \"created_at\": \"2020-03-30T23:07:05.547Z\",\n            \"updated_at\": \"2020-03-30T23:07:05.547Z\",\n            \"carrier\": \"USPS\",\n            \"status\": \"out_for_delivery\",\n            \"tracking_code\": \"EZ3000000003\",\n            \"public_url\": \"https://...\"\n        },\n        {\n            \"id\": \"trk_...\",\n            \"object\": \"Tracker\",\n            \"shipment_id\": \"shp_dab9ace301d690b3473d5e7f5c9dfba2\",\n            \"tracking_details\": [\n                {\n                    \"id\": null,\n                    \"object\": \"TrackingDetail\",\n                    \"created_at\": \"2020-03-30T23:08:45.296Z\",\n                    \"updated_at\": \"2020-03-30T23:08:45.296Z\",\n                    \"status\": \"pre_transit\",\n                    \"message\": \"Pre-Shipment Info Sent to USPS\",\n                    \"datetime\": \"2020-02-29T23:07:10.000Z\",\n                    \"source\": null,\n                    \"tracking_location\": {\n                        \"zip\": null,\n                        \"city\": null,\n                        \"state\": null,\n                        \"country\": null\n                    }\n                },\n                {\n                    \"id\": null,\n                    \"object\": \"TrackingDetail\",\n                    \"created_at\": \"2020-03-30T23:08:45.311Z\",\n                    \"updated_at\": \"2020-03-30T23:08:45.311Z\",\n                    \"status\": \"pre_transit\",\n                    \"message\": \"Shipping Label Created\",\n                    \"datetime\": \"2020-03-01T11:44:10.000Z\",\n                    \"source\": null,\n                    \"tracking_location\": {\n                        \"zip\": \"77063\",\n                        \"city\": \"HOUSTON\",\n                        \"state\": \"TX\",\n                        \"country\": null\n                    }\n                },\n                {\n                    \"id\": null,\n                    \"object\": \"TrackingDetail\",\n                    \"created_at\": \"2020-03-30T23:09:45.402Z\",\n                    \"updated_at\": \"2020-03-30T23:09:45.402Z\",\n                    \"status\": \"pre_transit\",\n                    \"message\": \"Pre-Shipment Info Sent to USPS\",\n                    \"datetime\": \"2020-02-29T23:08:10.000Z\",\n                    \"source\": null,\n                    \"tracking_location\": {\n                        \"zip\": null,\n                        \"city\": null,\n                        \"state\": null,\n                        \"country\": null\n                    }\n                },\n                {\n                    \"id\": null,\n                    \"object\": \"TrackingDetail\",\n                    \"created_at\": \"2020-03-30T23:09:45.416Z\",\n                    \"updated_at\": \"2020-03-30T23:09:45.416Z\",\n                    \"status\": \"pre_transit\",\n                    \"message\": \"Shipping Label Created\",\n                    \"datetime\": \"2020-03-01T11:45:10.000Z\",\n                    \"source\": null,\n                    \"tracking_location\": {\n                        \"zip\": \"77063\",\n                        \"city\": \"HOUSTON\",\n                        \"state\": \"TX\",\n                        \"country\": null\n                    }\n                },\n                {\n                    \"id\": null,\n                    \"object\": \"TrackingDetail\",\n                    \"created_at\": \"2020-03-30T23:09:45.430Z\",\n                    \"updated_at\": \"2020-03-30T23:09:45.430Z\",\n                    \"status\": \"in_transit\",\n                    \"message\": \"Arrived at USPS Origin Facility\",\n                    \"datetime\": \"2020-03-01T21:50:10.000Z\",\n                    \"source\": null,\n                    \"tracking_location\": {\n                        \"zip\": \"77315\",\n                        \"city\": \"NORTH HOUSTON\",\n                        \"state\": \"TX\",\n                        \"country\": null\n                    }\n                },\n                {\n                    \"id\": null,\n                    \"object\": \"TrackingDetail\",\n                    \"created_at\": \"2020-03-30T23:09:45.444Z\",\n                    \"updated_at\": \"2020-03-30T23:09:45.444Z\",\n                    \"status\": \"in_transit\",\n                    \"message\": \"Arrived at USPS Facility\",\n                    \"datetime\": \"2020-03-02T23:26:10.000Z\",\n                    \"source\": null,\n                    \"tracking_location\": {\n                        \"zip\": \"29201\",\n                        \"city\": \"COLUMBIA\",\n                        \"state\": \"SC\",\n                        \"country\": null\n                    }\n                },\n                {\n                    \"id\": null,\n                    \"object\": \"TrackingDetail\",\n                    \"created_at\": \"2020-03-30T23:09:45.470Z\",\n                    \"updated_at\": \"2020-03-30T23:09:45.470Z\",\n                    \"status\": \"in_transit\",\n                    \"message\": \"Arrived at Post Office\",\n                    \"datetime\": \"2020-03-03T02:17:10.000Z\",\n                    \"source\": null,\n                    \"tracking_location\": {\n                        \"zip\": \"29407\",\n                        \"city\": \"CHARLESTON\",\n                        \"state\": \"SC\",\n                        \"country\": null\n                    }\n                },\n                {\n                    \"id\": null,\n                    \"object\": \"TrackingDetail\",\n                    \"created_at\": \"2020-03-30T23:09:45.483Z\",\n                    \"updated_at\": \"2020-03-30T23:09:45.483Z\",\n                    \"status\": \"in_transit\",\n                    \"message\": \"Sorting Complete\",\n                    \"datetime\": \"2020-03-03T07:57:10.000Z\",\n                    \"source\": null,\n                    \"tracking_location\": {\n                        \"zip\": \"29407\",\n                        \"city\": \"CHARLESTON\",\n                        \"state\": \"SC\",\n                        \"country\": null\n                    }\n                },\n                {\n                    \"id\": null,\n                    \"object\": \"TrackingDetail\",\n                    \"created_at\": \"2020-03-30T23:10:45.659Z\",\n                    \"updated_at\": \"2020-03-30T23:10:45.659Z\",\n                    \"status\": \"pre_transit\",\n                    \"message\": \"Pre-Shipment Info Sent to USPS\",\n                    \"datetime\": \"2020-02-29T23:09:10.000Z\",\n                    \"source\": null,\n                    \"tracking_location\": {\n                        \"zip\": null,\n                        \"city\": null,\n                        \"state\": null,\n                        \"country\": null\n                    }\n                },\n                {\n                    \"id\": null,\n                    \"object\": \"TrackingDetail\",\n                    \"created_at\": \"2020-03-30T23:10:45.680Z\",\n                    \"updated_at\": \"2020-03-30T23:10:45.680Z\",\n                    \"status\": \"pre_transit\",\n                    \"message\": \"Shipping Label Created\",\n                    \"datetime\": \"2020-03-01T11:46:10.000Z\",\n                    \"source\": null,\n                    \"tracking_location\": {\n                        \"zip\": \"77063\",\n                        \"city\": \"HOUSTON\",\n                        \"state\": \"TX\",\n                        \"country\": null\n                    }\n                }\n            ],\n            \"created_at\": \"2020-03-30T23:07:09.998Z\",\n            \"updated_at\": \"2020-03-30T23:07:09.998Z\",\n            \"carrier\": \"USPS\",\n            \"status\": \"unknown\",\n            \"tracking_code\": \"9405....\",\n            \"public_url\": \"https://...\"\n        }\n    ],\n    \"has_more\": true\n}\n```\n\nRetrieve an paginated list of all Trackers available to the authenticated account.\n\n### HTTP Request\n\n`GET https://www.vanlo.com/api/v1/trackers`\n\n## Get a Tracker\n\n```shell\ncurl -X GET https://www.vanlo.com/api/v1/trackers/trk_... \\\n  -H 'Authorization: Bearer VANLO_API_KEY'\n```\n\n```ruby\nrequire 'vanlo'\nVanlo.api_key = 'VANLO_API_KEY'\n\nVanlo::Tracker.retrieve('trk_...')\n```\n\n```python\nimport vanlo\nvanlo.api_key = 'VANLO_API_KEY'\n\nvanlo.Tracker.retrieve('trk_...')\n```\n\n```php\nrequire_once('/path/to/lib/vanlo.php');\n\\Vanlo\\Vanlo::setApiKey('VANLO_API_KEY');\n\n$tracker = \\Vanlo\\Tracker::retrieve('trk_...');\n```\n\n```csharp\nusing Vanlo;\nClientManager.SetCurrent(\"VANLO_API_KEY\");\n\nTracker tracker = Tracker.Retrieve(\"trk_...\");\n```\n\n> The above command returns JSON structured like this:\n\n```json\n{\n    \"id\": \"trk_...\",\n    \"object\": \"Tracker\",\n    \"shipment_id\": null,\n    \"tracking_details\": [\n        {\n            \"id\": null,\n            \"object\": \"TrackingDetail\",\n            \"created_at\": \"2020-03-31T15:11:17.488Z\",\n            \"updated_at\": \"2020-03-31T15:11:17.488Z\",\n            \"status\": \"pre_transit\",\n            \"message\": \"Pre-Shipment information received\",\n            \"datetime\": \"2020-02-29T15:11:17.000Z\",\n            \"source\": null,\n            \"tracking_location\": {\n                \"zip\": null,\n                \"city\": null,\n                \"state\": null,\n                \"country\": null\n            }\n        },\n        {\n            \"id\": null,\n            \"object\": \"TrackingDetail\",\n            \"created_at\": \"2020-03-31T15:11:17.499Z\",\n            \"updated_at\": \"2020-03-31T15:11:17.499Z\",\n            \"status\": \"pre_transit\",\n            \"message\": \"Shipping label created\",\n            \"datetime\": \"2020-03-01T10:00:17.000Z\",\n            \"source\": null,\n            \"tracking_location\": {\n                \"zip\": null,\n                \"city\": null,\n                \"state\": null,\n                \"country\": null\n            }\n        },\n        {\n            \"id\": null,\n            \"object\": \"TrackingDetail\",\n            \"created_at\": \"2020-03-31T15:11:17.510Z\",\n            \"updated_at\": \"2020-03-31T15:11:17.510Z\",\n            \"status\": \"in_transit\",\n            \"message\": \"Picked Up\",\n            \"datetime\": \"2020-03-01T15:11:17.000Z\",\n            \"source\": null,\n            \"tracking_location\": {\n                \"zip\": \"94612\",\n                \"city\": \"Oakland\",\n                \"state\": \"CA\",\n                \"country\": \"US\"\n            }\n        },\n        {\n            \"id\": null,\n            \"object\": \"TrackingDetail\",\n            \"created_at\": \"2020-03-31T15:11:17.521Z\",\n            \"updated_at\": \"2020-03-31T15:11:17.521Z\",\n            \"status\": \"in_transit\",\n            \"message\": \"Arrived at Sort Facility\",\n            \"datetime\": \"2020-03-02T09:38:17.000Z\",\n            \"source\": null,\n            \"tracking_location\": {\n                \"zip\": \"60290\",\n                \"city\": \"Chicago\",\n                \"state\": \"IL\",\n                \"country\": \"US\"\n            }\n        },\n        {\n            \"id\": null,\n            \"object\": \"TrackingDetail\",\n            \"created_at\": \"2020-03-31T15:11:17.532Z\",\n            \"updated_at\": \"2020-03-31T15:11:17.532Z\",\n            \"status\": \"in_transit\",\n            \"message\": \"Departed Sort Facility\",\n            \"datetime\": \"2020-03-03T10:18:17.000Z\",\n            \"source\": null,\n            \"tracking_location\": {\n                \"zip\": \"60290\",\n                \"city\": \"Chicago\",\n                \"state\": \"IL\",\n                \"country\": \"US\"\n            }\n        },\n        {\n            \"id\": null,\n            \"object\": \"TrackingDetail\",\n            \"created_at\": \"2020-03-31T15:11:17.542Z\",\n            \"updated_at\": \"2020-03-31T15:11:17.542Z\",\n            \"status\": \"in_transit\",\n            \"message\": \"Arrived at Distribution Center\",\n            \"datetime\": \"2020-03-03T15:11:17.000Z\",\n            \"source\": null,\n            \"tracking_location\": {\n                \"zip\": \"19087\",\n                \"city\": \"Radnor\",\n                \"state\": \"PA\",\n                \"country\": \"US\"\n            }\n        }\n    ],\n    \"created_at\": \"2020-03-31T15:11:17.461Z\",\n    \"updated_at\": \"2020-03-31T15:11:17.461Z\",\n    \"carrier\": \"USPS\",\n    \"status\": \"in_transit\",\n    \"tracking_code\": \"EZ2...\",\n    \"public_url\": \"https://...\"\n}\n```\n\nThis endpoint retrieves a tracker.\n\n### HTTP Request\n\n`GET https://www.vanlo.com/api/v1/trackers/:id`\n\n### Tracker Errors\n\nTracker creation returns only the [common errors](/#errors). The one with a tracker-specific\ntrigger is `UNPROCESSABLE_ENTITY` 422, returned when the tracking code could not be tracked - an\nunknown or unsupported tracking number. See [Errors](/#errors) for the full list and how to\nhandle each one."
    },
    {
      "id": "partner_events",
      "title": "Partner Events",
      "content": "# Partner Events\n\nThe Partner Events endpoint reports USPS shipment milestones - such as a label being created,\na package departing a facility, or an order being received - back to the USPS. You submit an\nevent for one or more of your tracking codes, and Vanlo reports it to the USPS on your behalf.\n\nReporting is asynchronous. A successful request returns `202 Accepted`: Vanlo has accepted\nthe event for reporting, not confirmed that the USPS has already recorded it.\n\n## Create a Partner Event\n\n```shell\ncurl -X POST https://www.vanlo.com/api/v1/partner_events \\\n  -H 'Authorization: Bearer VANLO_API_KEY' \\\n  -H 'Content-Type: application/json' \\\n  -d '{\n        \"partner_event\": {\n          \"tracking_numbers\": [\"9400111899223333444459\"],\n          \"event_code\": \"80\",\n          \"event_zip5\": \"94103\",\n          \"event_date_time\": \"2026-07-06T14:30:00Z\"\n        }\n      }'\n```\n\n```ruby\nrequire 'vanlo'\nVanlo.api_key = 'VANLO_API_KEY'\n\nVanlo::PartnerEvent.create(\n  tracking_numbers: ['9400111899223333444459'],\n  event_code: '80',\n  event_zip5: '94103',\n  event_date_time: '2026-07-06T14:30:00Z'\n)\n```\n\n```python\nimport vanlo\nvanlo.api_key = 'VANLO_API_KEY'\n\nvanlo.PartnerEvent.create(\n  tracking_numbers=['9400111899223333444459'],\n  event_code='80',\n  event_zip5='94103',\n  event_date_time='2026-07-06T14:30:00Z'\n)\n```\n\n```php\nrequire_once('/path/to/lib/vanlo.php');\n\\Vanlo\\Vanlo::setApiKey('VANLO_API_KEY');\n\n$partner_event = \\Vanlo\\PartnerEvent::create(array(\n  'tracking_numbers' => array('9400111899223333444459'),\n  'event_code' => '80',\n  'event_zip5' => '94103',\n  'event_date_time' => '2026-07-06T14:30:00Z'\n));\n```\n\n```csharp\nusing Vanlo;\nClientManager.SetCurrent(\"VANLO_API_KEY\");\n\nvar parameters = new Dictionary<string, object>() {\n    { \"tracking_numbers\", new List<string>() { \"9400111899223333444459\" } },\n    { \"event_code\", \"80\" },\n    { \"event_zip5\", \"94103\" },\n    { \"event_date_time\", \"2026-07-06T14:30:00Z\" }\n};\n\nPartnerEvent partnerEvent = PartnerEvent.Create(parameters);\n```\n\n> A successful request returns an empty body with HTTP status `202 Accepted`.\n\nSubmits an event for one or more of your tracking codes. Each tracking code you pass is\nresolved to one of your shipments and reported to the USPS.\n\nOnly USPS shipments purchased through Vanlo can be reported. Tracking codes that are not\neligible - such as an unrecognized code, another carrier's shipment, or a USPS label not\nbought through Vanlo - are skipped rather than causing the whole request to fail. If\n**none** of the supplied tracking codes match a shipment on your account, the request\nreturns an error (see below). Duplicate and blank tracking codes are ignored.\n\nMost events default their scan location and time to the shipment's label data. Physical\nhandling events (codes `80`-`87`) happen at the partner rather than at label creation, so\nthey require you to supply the scan ZIP (`event_zip5`) and timestamp (`event_date_time`).\n\n### HTTP Request\n\n`POST https://www.vanlo.com/api/v1/partner_events`\n\n### Parameters\n\nParameter | Type | Specification\n--------- | ---- | -------------\ntracking_numbers | [string...] | **Required.** One or more tracking codes to report the event for. Must contain at least one value. Duplicates and blanks are ignored\nevent_code | string | The event being reported (default: `\"GX\"`). See [Event Codes](#event-codes) below\nevent_zip5 | string | 5-digit ZIP where the event occurred. **Required for physical handling events (`80`-`87`).** For other events it defaults to the shipment's origin (from-address) ZIP\nevent_date_time | string | Timestamp the event occurred, in ISO 8601 (e.g. `\"2026-07-03T12:00:00Z\"`). Must be a valid timestamp. **Required for physical handling events (`80`-`87`).** For other events it defaults to the shipment's label timestamp\n\n### Event Codes\n\nCode | Description\n---- | -----------\nGX | Label Created (default)\nAL | Acceptance / label event\n80 | Picked up by shipping partner\n81 | Arrived shipping partner facility\n82 | Departed shipping partner facility\n83 | Tendered to Postal Service\n84 | Arrived agent facility\n85 | Departed agent facility\n86 | Delivered by Agent to Merchant\n87 | Final Disposition by Agent\n89 | Pre-Shipment Notification, Order Received by Merchant\n\nCodes `84`-`87` are for use with Parcel Returns Service only.\n\nCodes `80`-`87` are physical handling scans, so they require `event_zip5` and\n`event_date_time`. Supplying any other event code returns an `UNPROCESSABLE_ENTITY` error\nlisting the supported codes.\n\n### Response\n\nOn success the endpoint returns HTTP `202 Accepted` with an empty body. The event is reported\nto the USPS shortly after the response, and retried automatically if the USPS is unavailable -\nan accepted event never needs to be re-submitted.\n\nBecause ineligible tracking codes are skipped, a `202 Accepted` confirms that your request was\naccepted, not that every tracking code you sent was reported.\n\n### Partner Event Errors\n\nPartner Event creation returns the [common errors](/#errors), plus these\nendpoint-specific triggers:\n\nError | Status | Trigger\n----- | ------ | -------\nBAD_REQUEST | 400 | `tracking_numbers` is empty, `event_date_time` is not a valid timestamp, or a physical handling event (`80`-`87`) is missing `event_zip5` or `event_date_time`. Field-level details are in `error.errors`\nUNPROCESSABLE_ENTITY | 422 | The `event_code` is not supported, or none of the supplied tracking codes matched a shipment on your account\n\nSee [Errors](/#errors) for the full list and how to handle each one."
    },
    {
      "id": "webhooks",
      "title": "Webhooks",
      "content": "# Webhooks\n\n<aside class=\"endpoint-guide-link\">\n  <svg width=\"20\" height=\"20\" viewBox=\"0 0 16.5 15.5\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\"><path d=\"M0.75 1.75C0.75 1.19772 1.19772 0.75 1.75 0.75H5.25035C6.04606 0.75 6.80918 1.0819 7.37184 1.67269C7.93449 2.26347 8.25059 3.06475 8.25059 3.90025V14.75C8.25059 14.1234 8.01352 13.6985 7.59153 13.2554C7.16954 12.8124 6.59719 12.5634 6.00041 12.5634H1.75C1.19771 12.5634 0.75 12.1157 0.75 11.5634V1.75Z\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/><path d=\"M15.7506 1.75C15.7506 1.19772 15.3029 0.75 14.7506 0.75H11.2502C10.4545 0.75 9.6914 1.0819 9.12875 1.67269C8.5661 2.26347 8.25 3.06475 8.25 3.90025V14.75C8.25 14.1234 8.48707 13.6985 8.90906 13.2554C9.33105 12.8124 9.90339 12.5634 10.5002 12.5634H14.7506C15.3029 12.5634 15.7506 12.1157 15.7506 11.5634V1.75Z\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/></svg>\n  <span>Setting up webhooks? The <a href=\"#\" class=\"api-guides-btn\" data-guide=\"webhooks\">Webhooks Guide</a> covers registration, signature verification, and handling event payloads.</span>\n</aside>\n\n## Webhook Object\n\nParameter | Type | Specification\n--------- | ----- | -----\nid | string | Unique identifier, begins with \"hook_\"\nobject | string | \"Webhook\"\nurl | string | The URL that webhook notifications are sent to\ndisabled_at | datetime | The time the webhook was disabled, or null if enabled\n\n## Create a Webhook\n\n```shell\ncurl -X POST https://www.vanlo.com/api/v1/webhooks \\\n  -H 'Authorization: Bearer VANLO_API_KEY' \\\n  -d 'webhook[url]=https://example.com'\n```\n\n```ruby\nrequire 'vanlo'\nVanlo.api_key = 'VANLO_API_KEY'\n\nVanlo::Webhook.create(url: 'https://example.com')\n```\n\n```python\nimport vanlo\nvanlo.api_key = 'VANLO_API_KEY'\n\nvanlo.Webhook.create(url='https://example.com')\n\n```\n\n```php\nrequire_once('/path/to/lib/vanlo.php');\n\\Vanlo\\Vanlo::setApiKey('VANLO_API_KEY');\n\n$webhook = \\Vanlo\\Webhook::create(array('url' => 'https://example.com'));\n```\n\n```csharp\nusing Vanlo;\nClientManager.SetCurrent(\"VANLO_API_KEY\");\n\nWebhook webhook = Webhook.Create(\n    new Dictionary<string, object>() {\n        { \"url\", \"example.com\" }\n    }\n);\n```\n\n> The above command returns JSON structured like this:\n\n```json\n{\n   \"id\":\"hook_...\",\n   \"object\":\"Webhook\",\n   \"url\":\"http://example.com\",\n   \"disabled_at\":null\n}\n```\n\nTo create a Webhook, you simply need to provide a url parameter that you wish to receive notifications to.\n\n### HTTP Request\n\n`POST https://www.vanlo.com/api/v1/webhooks`\n\n### Create Webhook Request Parameters\n\nParameter | Type | Specification\n--------- | ----- | -----\nurl | string | The URL to receive webhook notifications\n\n## Retrieve a list of Webhooks\n\n```shell\ncurl -X GET https://www.vanlo.com/api/v1/webhooks \\\n  -H 'Authorization: Bearer VANLO_API_KEY'\n```\n\n```ruby\nrequire 'vanlo'\nVanlo.api_key = 'VANLO_API_KEY'\n\nVanlo::Webhook.all\n```\n\n```python\nimport vanlo\nvanlo.api_key = 'VANLO_API_KEY'\n\nwebhooks = vanlo.Webhook.all()\n```\n\n```php\nrequire_once('/path/to/lib/vanlo.php');\n\\Vanlo\\Vanlo::setApiKey('VANLO_API_KEY');\n\n$webhooks = \\Vanlo\\Webhook::all();\n```\n\n```csharp\nusing Vanlo;\nClientManager.SetCurrent(\"VANLO_API_KEY\");\n\nList<Webhook> webhooks = Webhook.List();\n```\n\nRetrieve an unpaginated list of all Webhooks available to the authenticated account.\n\n> The above command returns JSON structured like this:\n\n```json\n{\n   \"webhooks\":[\n      {\n         \"id\":\"hook_...\",\n         \"object\":\"Webhook\",\n         \"url\":\"https://webhooks.example.com\",\n         \"disabled_at\":null\n      },\n      {\n         \"id\":\"hook_...\",\n         \"object\":\"Webhook\",\n         \"url\":\"http://example.com\",\n         \"disabled_at\":null\n      }\n   ]\n}\n\n```\n\n### HTTP Request\n\n`GET https://www.vanlo.com/api/v1/webhooks`\n\n## Retrieve a Webhook\n\n```shell\ncurl -X GET https://www.vanlo.com/api/v1/webhooks/hook_... \\\n  -H 'Authorization: Bearer VANLO_API_KEY'\n```\n\n```ruby\nrequire 'vanlo'\nVanlo.api_key = 'VANLO_API_KEY'\n\nVanlo::Webhook.retrieve('hook_...')\n```\n\n```python\nimport vanlo\nvanlo.api_key = 'VANLO_API_KEY'\n\nvanlo.Webhook.retrieve('hook...')\n```\n\n```php\nrequire_once('/path/to/lib/vanlo.php');\n\\Vanlo\\Vanlo::setApiKey('VANLO_API_KEY');\n\n$webhook = \\Vanlo\\Webhook::retrieve(\"hook_...\");\n```\n\n```csharp\nusing Vanlo;\nClientManager.SetCurrent(\"VANLO_API_KEY\");\n\nWebhook webhook = Webhook.Retrieve(\"hook_...\");\n```\n\n> The above command returns JSON structured like this:\n\n```json\n{\n   \"id\":\"hook_...\",\n   \"object\":\"Webhook\",\n   \"url\":\"http://example.com\",\n   \"disabled_at\":null\n}\n```\n\nRetrieve a Webhook by id.\n\n### HTTP Request\n\n`GET https://www.vanlo.com/api/v1/webhooks/:id`\n\n### Retrieve Webhook Request Parameters\n\nParameter | Type | Specification\n--------- | ----- | -----\nid | string | Unique identifier of the webhook\n\n## Update a Webhook\n\n```shell\ncurl -X PUT https://www.vanlo.com/api/v1/webhooks/hook_... \\\n  -H 'Authorization: Bearer VANLO_API_KEY'\n```\n\n```ruby\nrequire 'vanlo'\nVanlo.api_key = 'VANLO_API_KEY'\n\nwebhook = Vanlo::Webhook.retrieve('hook_...')\nwebhook.update\n```\n\n```python\nimport vanlo\nvanlo.api_key = 'VANLO_API_KEY'\n\nwebhook = vanlo.Webhook.retrieve('hook_...')\nwebhook.update()\n```\n\n```php\nrequire_once('/path/to/lib/vanlo.php');\n\\Vanlo\\Vanlo::setApiKey('VANLO_API_KEY');\n\n$webhook = \\Vanlo\\Webhook::retrieve(\"hook_...\");\n$webhook->update();\n```\n\n```csharp\nusing Vanlo;\nClientManager.SetCurrent(\"VANLO_API_KEY\");\n\nWebhook webhook = Webhook.Retrieve(\"hook_...\");\nwebhook.Update();\n```\n\n> The above command returns JSON structured like this:\n\n```json\n{\n   \"id\":\"hook_...\",\n   \"object\":\"Webhook\",\n   \"url\":\"http://example.com\",\n   \"disabled_at\":null\n}\n```\n\nEnables a Webhook that has been disabled.\n\n### HTTP Request\n\n`PUT https://www.vanlo.com/api/v1/webhooks/:id`\n\n## Delete a Webhook\n\n```shell\ncurl -X DELETE https://www.vanlo.com/api/v1/webhooks/hook_... \\\n  -H 'Authorization: Bearer VANLO_API_KEY'\n```\n\n```ruby\nrequire 'vanlo'\nVanlo.api_key = 'VANLO_API_KEY'\n\nwebhook = Vanlo::Webhook.retrieve('hook_...')\nwebhook.delete\n```\n\n```python\nimport vanlo\nvanlo.api_key = 'VANLO_API_KEY'\n\nwebhook = vanlo.Webhook.retrieve('hook_...')\nwebhook.delete()\n```\n\n```php\nrequire_once('/path/to/lib/vanlo.php');\n\\Vanlo\\Vanlo::setApiKey('VANLO_API_KEY');\n\n$webhook->delete();\n```\n\n```csharp\nusing Vanlo;\nClientManager.SetCurrent(\"VANLO_API_KEY\");\n\nWebhook webhook = Webhook.Retrieve(\"hook_...\");\nwebhook.Destroy();\n```\n\nDelete a Webhook by id.\n\n> The above command returns JSON structured like this:\n\n```json\n{}\n```\n\n### HTTP Request\n\n`DELETE https://www.vanlo.com/api/v1/webhooks/:id`\n\n### Delete Webhook Request Parameters\n\nParameter | Type | Specification\n--------- | ----- | -----\nid | string | Unique identifier of the webhook\n\n## Handling Webhooks\n\nWhen a webhook is triggered an event will be sent to the webhook endpoint via a POST request.\n\n### Webhook Event Parameters\n\nParameter | Type | Specification\n--------- | ----- | -----\nid | string | Unique identifier, begins with \"evt_\"\nobject | string | \"Event\"\ndescription | string | Event type descriptor, e.g. \"tracker.updated\" or \"batch.updated\"\npending_urls | [string...] | Array of webhook URLs that have not yet received this event\ncompleted_urls | [string...] | Array of webhook URLs that have successfully received this event\nfailed_urls | [string...] | Array of webhook URLs that failed to receive this event\nresult | [\\<Result\\>](/#webhook-event-result) | The event payload, varies by event type\ncreated_at | datetime | When the event was created\nupdated_at | datetime | When the event was last updated\n\n### Webhook Errors\n\nCreating or updating a webhook returns the [common errors](/#errors) shared by every\nendpoint - mainly parameter validation: invalid webhook fields (for example a malformed\nURL) return `BAD_REQUEST` 400, and `INVALID_PARAMETERS` 422 for other malformed input. See\n[Errors](/#errors) for the full list and how to handle each one."
    },
    {
      "id": "webhook_events",
      "title": "Webhook Events",
      "content": "## Event Object\n\nParameter | Type | Specification\n--------- | ----- | -----\nid | string | Unique identifier, begins with \"evt_\"\nobject | string | \"Event\"\ndescription | string | Event type descriptor, e.g. \"tracker.updated\" or \"batch.updated\"\npending_urls | [string...] | Array of webhook URLs that have not yet received this event\ncompleted_urls | [string...] | Array of webhook URLs that have successfully received this event\nfailed_urls | [string...] | Array of webhook URLs that failed to receive this event\nresult | object | The event payload, varies by event type (see below)\ncreated_at | datetime | When the event was created\nupdated_at | datetime | When the event was last updated\n\n> Example webhook event payload:\n\n```json\n{\n  \"id\": \"evt_...\",\n  \"object\": \"Event\",\n  \"description\": \"tracker.updated\",\n  \"pending_urls\": [\"https://example.com/webhooks\"],\n  \"completed_urls\": [],\n  \"failed_urls\": [],\n  \"result\": {\n    \"id\": \"trk_...\",\n    \"object\": \"Tracker\",\n    \"status\": \"in_transit\",\n    \"carrier\": \"USPS\",\n    \"tracking_code\": \"9405500205903028777744\",\n    \"shipment_id\": \"shp_...\",\n    \"tracking_details\": [\n      {\n        \"object\": \"TrackingDetail\",\n        \"status\": \"in_transit\",\n        \"message\": \"Arrived at Sort Facility\",\n        \"datetime\": \"2020-04-13T13:58:52Z\",\n        \"source\": \"USPS\",\n        \"tracking_location\": {\n          \"zip\": \"60290\",\n          \"city\": \"Chicago\",\n          \"state\": \"IL\",\n          \"country\": \"US\"\n        }\n      }\n    ],\n    \"created_at\": \"2020-04-13T13:55:52Z\",\n    \"updated_at\": \"2020-04-13T13:58:52Z\",\n    \"public_url\": \"https://...\"\n  },\n  \"created_at\": \"2020-04-13T14:00:27Z\",\n  \"updated_at\": \"2020-04-13T14:00:27Z\"\n}\n```\n\n## Webhook Event Result\n\nEach event type returns a specific set of data in the `result` field of the webhook event. The events below are the v1 events Vanlo sends, grouped by the object they describe. Every payload follows the EasyPost object format, so handlers written for EasyPost webhooks parse Vanlo events without changes.\n\nDelivery, retry, and authentication are the same for every event - see the <a href=\"#webhooks-guide\">Webhooks Guide</a> for those details. The sections below document only the `result` payload of each event.\n\n## Batch Events\n\n### batch.created\n\nSent when a new batch is created. The `result` is a [Batch](/#batch-object) object, in the EasyPost format.\n\nParameter | Type | Specification\n--------- | ----- | -----\nid | string | Unique identifier of the batch, begins with \"batch_\"\nobject | string | \"Batch\"\nstate | string | Current state of the batch: \"creating\", \"created\", \"purchasing\", \"purchased\", \"purchase_failed\", \"label_generating\", or \"label_generated\"\nstatus | [\\<BatchStatus\\>](/#batchstatus-object) | Counts of shipments in each processing state\nnum_shipments | integer | Total number of shipments in the batch\nlabel_url | string | URL of the consolidated label, if generated\nscan_form | [\\<ScanForm\\>](/#scan-forms) | Associated ScanForm object, if generated\nshipments | [[\\<BatchShipment\\>](/#batchshipment-object)...] | Array of BatchShipment objects\ncreated_at | datetime | When the batch was created\nupdated_at | datetime | When the batch was last updated\n\n> Example `result` payload:\n\n```json\n{\n  \"id\": \"batch_...\",\n  \"object\": \"Batch\",\n  \"state\": \"created\",\n  \"num_shipments\": 2,\n  \"label_url\": null,\n  \"scan_form\": null,\n  \"shipments\": [\n    {\n      \"id\": \"shp_...\",\n      \"batch_status\": \"created\",\n      \"batch_message\": null,\n      \"tracking_code\": \"9405500205903028777744\"\n    }\n  ],\n  \"status\": {\n    \"created\": 2,\n    \"queued_for_purchase\": 0,\n    \"creation_failed\": 0,\n    \"postage_purchased\": 0,\n    \"postage_purchase_failed\": 0\n  },\n  \"created_at\": \"2026-06-03T14:00:27Z\",\n  \"updated_at\": \"2026-06-03T14:00:27Z\"\n}\n```\n\n### batch.updated\n\nSent whenever a batch changes - shipments are added or removed, postage is purchased, or a label is generated. The `result` is the same [Batch](/#batch-object) object as `batch.created`.\n\n> Example `result` payload:\n\n```json\n{\n  \"id\": \"batch_...\",\n  \"object\": \"Batch\",\n  \"state\": \"purchasing\",\n  \"num_shipments\": 2,\n  \"label_url\": null,\n  \"scan_form\": null,\n  \"shipments\": [\n    {\n      \"id\": \"shp_...\",\n      \"batch_status\": \"postage_purchased\",\n      \"batch_message\": null,\n      \"tracking_code\": \"9405500205903028777744\"\n    }\n  ],\n  \"status\": {\n    \"created\": 0,\n    \"queued_for_purchase\": 1,\n    \"creation_failed\": 0,\n    \"postage_purchased\": 1,\n    \"postage_purchase_failed\": 0\n  },\n  \"created_at\": \"2026-06-03T14:00:27Z\",\n  \"updated_at\": \"2026-06-03T14:02:10Z\"\n}\n```\n\n### batch.completed\n\nSent when every shipment in a batch has finished processing. The `result` is the same [Batch](/#batch-object) object as `batch.created`, with a final state (`purchased` or `label_generated`).\n\n> Example `result` payload:\n\n```json\n{\n  \"id\": \"batch_...\",\n  \"object\": \"Batch\",\n  \"state\": \"label_generated\",\n  \"num_shipments\": 2,\n  \"label_url\": \"https://vanlo-labels.s3.amazonaws.com/batch_label.pdf\",\n  \"scan_form\": null,\n  \"shipments\": [\n    {\n      \"id\": \"shp_...\",\n      \"batch_status\": \"postage_purchased\",\n      \"batch_message\": null,\n      \"tracking_code\": \"9405500205903028777744\"\n    }\n  ],\n  \"status\": {\n    \"created\": 0,\n    \"queued_for_purchase\": 0,\n    \"creation_failed\": 0,\n    \"postage_purchased\": 2,\n    \"postage_purchase_failed\": 0\n  },\n  \"created_at\": \"2026-06-03T14:00:27Z\",\n  \"updated_at\": \"2026-06-03T14:08:44Z\"\n}\n```\n\n## Tracker Events\n\n### tracker.updated\n\nSent whenever a tracker receives new tracking information from the carrier. The `result` is a [Tracker](/#tracker-object) object, in the EasyPost format.\n\nParameter | Type | Specification\n--------- | ----- | -----\nid | string | Unique identifier of the tracker, begins with \"trk_\"\nobject | string | \"Tracker\"\nstatus | string | Current tracking status: \"unknown\", \"pre_transit\", \"in_transit\", \"out_for_delivery\", \"delivered\", \"return_to_sender\", \"failure\", or \"error\"\ncarrier | string | Name of the carrier handling the shipment\ntracking_code | string | The tracking code provided by the carrier\nshipment_id | string | Identifier of the associated shipment, if any\ntracking_details | [[\\<TrackingDetails\\>](/#trackingdetails-object)...] | Array of TrackingDetail objects\ncarrier_detail | [\\<CarrierDetail\\>](/#carrierdetail-object) | Associated CarrierDetail object\npublic_url | string | URL to a publicly-accessible tracking page\nest_delivery_date | datetime | Estimated delivery date, if available\nsigned_by | string | Name of the person who signed for the package, if available\nweight | float | Weight of the package, if available\ncreated_at | datetime | When the tracker was created\nupdated_at | datetime | When the tracker was last updated\n\n> Example `result` payload:\n\n```json\n{\n  \"id\": \"trk_...\",\n  \"object\": \"Tracker\",\n  \"status\": \"in_transit\",\n  \"carrier\": \"USPS\",\n  \"tracking_code\": \"9405500205903028777744\",\n  \"shipment_id\": \"shp_...\",\n  \"tracking_details\": [\n    {\n      \"object\": \"TrackingDetail\",\n      \"status\": \"in_transit\",\n      \"message\": \"Arrived at Sort Facility\",\n      \"datetime\": \"2026-06-03T13:58:52Z\",\n      \"source\": \"USPS\",\n      \"tracking_location\": {\n        \"zip\": \"60290\",\n        \"city\": \"Chicago\",\n        \"state\": \"IL\",\n        \"country\": \"US\"\n      }\n    }\n  ],\n  \"est_delivery_date\": \"2026-06-05T00:00:00Z\",\n  \"signed_by\": null,\n  \"weight\": 16.0,\n  \"public_url\": \"https://...\",\n  \"created_at\": \"2026-06-03T13:55:52Z\",\n  \"updated_at\": \"2026-06-03T13:58:52Z\"\n}\n```\n\n### tracker.detail.created\n\nSent for each new tracking detail (carrier scan) added to a tracker. The `result` is a single tracking detail, with the parent tracker's `tracker_id` and `tracking_code` attached.\n\nParameter | Type | Specification\n--------- | ----- | -----\nstatus | string | Tracking status reported by this scan\nmessage | string | Human-readable description of the scan\ndatetime | datetime | When the carrier recorded the scan\nlocation | object | Scan location: `city`, `state`, `country`, `zip`\npod_urls | [string...] | Proof-of-delivery image URLs, if any\ncreated_at | datetime | When the detail was recorded\ntracker_id | string | Identifier of the parent tracker, begins with \"trk_\"\ntracking_code | string | The tracking code of the parent tracker\n\n> Example `result` payload:\n\n```json\n{\n  \"status\": \"in_transit\",\n  \"message\": \"Arrived at Sort Facility\",\n  \"datetime\": \"2026-06-03T13:58:52Z\",\n  \"location\": {\n    \"city\": \"Chicago\",\n    \"state\": \"IL\",\n    \"country\": \"US\",\n    \"zip\": \"60290\"\n  },\n  \"pod_urls\": [],\n  \"created_at\": \"2026-06-03T13:58:55Z\",\n  \"tracker_id\": \"trk_...\",\n  \"tracking_code\": \"9405500205903028777744\"\n}\n```\n\n## Refund Events\n\n### refund.successful\n\nSent when a refund request for a shipment is approved by the carrier. The `result` is a Refund object, in the EasyPost format.\n\nParameter | Type | Specification\n--------- | ----- | -----\nid | string | Unique identifier of the refund, begins with \"rfnd_\"\nobject | string | \"Refund\"\nstatus | string | \"refunded\"\nshipment_id | string | Identifier of the refunded shipment, begins with \"shp_\"\ncarrier | string | Name of the carrier that issued the refund\ntracking_code | string | The tracking code of the refunded shipment\ncreated_at | datetime | When the refund was created\nupdated_at | datetime | When the refund was last updated\n\n> Example `result` payload:\n\n```json\n{\n  \"id\": \"rfnd_...\",\n  \"object\": \"Refund\",\n  \"status\": \"refunded\",\n  \"shipment_id\": \"shp_...\",\n  \"carrier\": \"USPS\",\n  \"tracking_code\": \"9405500205903028777744\",\n  \"created_at\": \"2026-06-03T14:00:27Z\",\n  \"updated_at\": \"2026-06-03T14:05:00Z\"\n}\n```\n\n## Insurance Events\n\n### insurance.purchased\n\nSent when insurance is successfully purchased for a shipment. The `result` is an Insurance object, in the EasyPost format.\n\nParameter | Type | Specification\n--------- | ----- | -----\nid | string | Unique identifier of the insurance, begins with \"ins_\"\nobject | string | \"Insurance\"\nreference | string | Your reference for the insurance, if provided\namount | string | Insured value of the shipment\nprovider | string | Name of the insurance provider\nprovider_id | string | The provider's identifier for the policy\nstatus | string | Current status of the insurance\nmessages | [string...] | Provider messages, if any\nshipment_id | string | Identifier of the insured shipment, begins with \"shp_\"\ntracking_code | string | The tracking code of the insured shipment\ntracker | [\\<Tracker\\>](/#tracker-object) | Associated Tracker object, if available\nfrom_address | [\\<Address\\>](/#address-object) | Origin address, if available\nto_address | [\\<Address\\>](/#address-object) | Destination address, if available\nfee | object | Insurance fee: `amount`\ncreated_at | datetime | When the insurance was created\nupdated_at | datetime | When the insurance was last updated\n\n> Example `result` payload:\n\n```json\n{\n  \"id\": \"ins_...\",\n  \"object\": \"Insurance\",\n  \"reference\": \"INS-12345\",\n  \"amount\": \"100.00\",\n  \"provider\": \"InsureShield\",\n  \"provider_id\": \"PROV-987\",\n  \"status\": \"purchased\",\n  \"messages\": [],\n  \"shipment_id\": \"shp_...\",\n  \"tracking_code\": \"9405500205903028777744\",\n  \"tracker\": {\n    \"id\": \"trk_...\",\n    \"object\": \"Tracker\",\n    \"status\": \"pre_transit\"\n  },\n  \"from_address\": {\n    \"id\": \"adr_...\",\n    \"object\": \"Address\"\n  },\n  \"to_address\": {\n    \"id\": \"adr_...\",\n    \"object\": \"Address\"\n  },\n  \"fee\": {\n    \"amount\": \"1.50\"\n  },\n  \"created_at\": \"2026-06-03T14:00:27Z\",\n  \"updated_at\": \"2026-06-03T14:00:27Z\"\n}\n```\n\n## Payment Events\n\n### payment.created\n\nSent when a payment (account recharge) is initiated. The `result` is a PaymentLog object, in the EasyPost format.\n\nParameter | Type | Specification\n--------- | ----- | -----\nid | string | Unique identifier of the payment log, begins with \"paylog_\"\nobject | string | \"PaymentLog\"\namount | string | Charged amount\nbalance | string | Account balance after the payment, if available\nstatus | string | Payment status: \"pending\", \"completed\", or \"failed\"\nsource_type | string | Funding source: \"bank_account\" or \"credit_card\"\ntarget_type | string | \"vanlo_stripe\"\ncharge_type | string | \"recharge\"\ncreated_at | datetime | When the payment was created\n\n> Example `result` payload:\n\n```json\n{\n  \"id\": \"paylog_...\",\n  \"object\": \"PaymentLog\",\n  \"amount\": \"25.00\",\n  \"balance\": \"150.00\",\n  \"status\": \"pending\",\n  \"source_type\": \"bank_account\",\n  \"target_type\": \"vanlo_stripe\",\n  \"charge_type\": \"recharge\",\n  \"created_at\": \"2026-06-03T14:00:27Z\"\n}\n```\n\n### payment.completed\n\nSent when a payment clears. The `result` is the same PaymentLog object as `payment.created`, with `status` set to `completed`.\n\n> Example `result` payload:\n\n```json\n{\n  \"id\": \"paylog_...\",\n  \"object\": \"PaymentLog\",\n  \"amount\": \"25.00\",\n  \"balance\": \"175.00\",\n  \"status\": \"completed\",\n  \"source_type\": \"bank_account\",\n  \"target_type\": \"vanlo_stripe\",\n  \"charge_type\": \"recharge\",\n  \"created_at\": \"2026-06-03T14:00:27Z\"\n}\n```\n\n### payment.failed\n\nSent when a payment fails. The `result` is the same PaymentLog object as `payment.created`, with `status` set to `failed`.\n\n> Example `result` payload:\n\n```json\n{\n  \"id\": \"paylog_...\",\n  \"object\": \"PaymentLog\",\n  \"amount\": \"25.00\",\n  \"balance\": \"150.00\",\n  \"status\": \"failed\",\n  \"source_type\": \"bank_account\",\n  \"target_type\": \"vanlo_stripe\",\n  \"charge_type\": \"recharge\",\n  \"created_at\": \"2026-06-03T14:00:27Z\"\n}\n```\n\n## Report Events\n\n### report.new\n\nSent when a report is requested and starts generating. The `result` is a Report object, in the EasyPost format.\n\nParameter | Type | Specification\n--------- | ----- | -----\nid | string | Unique identifier of the report, begins with \"report_\"\nobject | string | The report type, e.g. \"ShipmentReport\" or \"PaymentReport\"\nstatus | string | Report status: \"new\", \"available\", or \"failed\"\nstart_date | date | First day covered by the report\nend_date | date | Last day covered by the report\nurl | string | Download URL, present once the report is available\nurl_expires_at | datetime | When the download URL expires, present once available\ncreated_at | datetime | When the report was created\nupdated_at | datetime | When the report was last updated\n\n> Example `result` payload:\n\n```json\n{\n  \"id\": \"report_...\",\n  \"object\": \"ShipmentReport\",\n  \"status\": \"new\",\n  \"start_date\": \"2026-05-01\",\n  \"end_date\": \"2026-05-31\",\n  \"url\": null,\n  \"url_expires_at\": null,\n  \"created_at\": \"2026-06-03T14:00:27Z\",\n  \"updated_at\": \"2026-06-03T14:00:27Z\"\n}\n```\n\n### report.available\n\nSent when a report has finished generating and is ready to download. The `result` is the same Report object as `report.new`, with `status` set to `available` and the `url` populated.\n\n> Example `result` payload:\n\n```json\n{\n  \"id\": \"report_...\",\n  \"object\": \"ShipmentReport\",\n  \"status\": \"available\",\n  \"start_date\": \"2026-05-01\",\n  \"end_date\": \"2026-05-31\",\n  \"url\": \"https://vanlo-reports.s3.amazonaws.com/report.csv.zip\",\n  \"url_expires_at\": \"2026-06-03T15:00:27Z\",\n  \"created_at\": \"2026-06-03T14:00:27Z\",\n  \"updated_at\": \"2026-06-03T14:03:11Z\"\n}\n```\n\n### report.failed\n\nSent when a report fails to generate. The `result` is the same Report object as `report.new`, with `status` set to `failed`.\n\n> Example `result` payload:\n\n```json\n{\n  \"id\": \"report_...\",\n  \"object\": \"ShipmentReport\",\n  \"status\": \"failed\",\n  \"start_date\": \"2026-05-01\",\n  \"end_date\": \"2026-05-31\",\n  \"url\": null,\n  \"url_expires_at\": null,\n  \"created_at\": \"2026-06-03T14:00:27Z\",\n  \"updated_at\": \"2026-06-03T14:03:11Z\"\n}\n```\n\n## Shipment Events\n\n### shipment.invoice.created\n\nSent when a carrier issues a post-purchase adjustment (a corrected charge) for a shipment. The `result` is a ShipmentInvoice object, in the EasyPost format.\n\nParameter | Type | Specification\n--------- | ----- | -----\nid | string | Unique identifier of the invoice, begins with \"ppshp_\"\nobject | string | \"ShipmentInvoice\"\nstatus | string | \"processed\"\nshipment_id | string | Identifier of the adjusted shipment, begins with \"shp_\"\ntracking_code | string | The tracking code of the adjusted shipment\nlabel_date | date | Date the label was created\nquoted_amount | string | Amount originally quoted for the shipment\nquoted_currency | string | \"USD\"\ninitially_paid_amount | string | Amount initially paid for the shipment\ninitially_paid_currency | string | \"USD\"\ninitially_paid_payment_log | string | Payment log identifier for the initial charge\nadjustment_reason | string | Reason for the adjustment, e.g. \"weight_correction\"\nadjustment_amount | string | Amount of the adjustment\ninvoice_date | datetime | When the adjustment was recorded\ninvoice_type | string | \"adjustment\"\ninvoice_amount | string | Amount of the invoice\ninvoice_currency | string | \"USD\"\ninvoice_payment_log | string | Payment log identifier for the adjustment charge\ntotal_cost | string | Total cost after the adjustment\ncarrier_account | string | Identifier of the carrier account used\ncarrier | string | Name of the carrier\nclaimed_details | object | Details the carrier claimed for the shipment\ncaptured_details | object | Details Vanlo captured at purchase time\n\n> Example `result` payload:\n\n```json\n{\n  \"id\": \"ppshp_...\",\n  \"object\": \"ShipmentInvoice\",\n  \"status\": \"processed\",\n  \"shipment_id\": \"shp_...\",\n  \"tracking_code\": \"9405500205903028777744\",\n  \"label_date\": \"2026-05-20\",\n  \"quoted_amount\": \"8.50\",\n  \"quoted_currency\": \"USD\",\n  \"initially_paid_amount\": \"8.50\",\n  \"initially_paid_currency\": \"USD\",\n  \"initially_paid_payment_log\": \"paylog_...\",\n  \"adjustment_reason\": \"weight_correction\",\n  \"adjustment_amount\": \"2.00\",\n  \"invoice_date\": \"2026-06-03T14:00:27Z\",\n  \"invoice_type\": \"adjustment\",\n  \"invoice_amount\": \"2.00\",\n  \"invoice_currency\": \"USD\",\n  \"invoice_payment_log\": \"paylog_...\",\n  \"total_cost\": \"10.50\",\n  \"carrier_account\": \"ca_...\",\n  \"carrier\": \"USPS\",\n  \"claimed_details\": {},\n  \"captured_details\": {}\n}\n```\n\n## Scan Form Events\n\n### scan_form.updated\n\nSent when a scan form is created or its status changes. The `result` is a ScanForm object, in the EasyPost format.\n\nParameter | Type | Specification\n--------- | ----- | -----\nid | string | Unique identifier of the scan form, begins with \"sf_\"\nobject | string | \"ScanForm\"\nstatus | string | Current status of the scan form\ntracking_codes | [string...] | Tracking codes included on the form\naddress | [\\<Address\\>](/#address-object) | Origin address, if available\nmessage | string | Error message, if the form failed\nwarnings | [string...] | Warnings, present when the form is ready with warnings\nbatch_id | string | Identifier of the associated batch, if any, begins with \"batch_\"\nreference | string | Your reference for the scan form, if provided\nform_url | string | URL of the generated scan form document\ncreated_at | datetime | When the scan form was created\nupdated_at | datetime | When the scan form was last updated\n\n> Example `result` payload:\n\n```json\n{\n  \"id\": \"sf_...\",\n  \"object\": \"ScanForm\",\n  \"status\": \"ready\",\n  \"tracking_codes\": [\"9405500205903028777744\"],\n  \"address\": {\n    \"id\": \"adr_...\",\n    \"object\": \"Address\"\n  },\n  \"message\": null,\n  \"batch_id\": \"batch_...\",\n  \"reference\": \"SF-001\",\n  \"form_url\": \"https://vanlo-scanforms.s3.amazonaws.com/scan_form.pdf\",\n  \"created_at\": \"2026-06-03T14:00:27Z\",\n  \"updated_at\": \"2026-06-03T14:00:27Z\"\n}\n```\n\n### Webhook Event Errors\n\nThese are the event payloads Vanlo delivers to your webhook URLs and lists through the\n[Events](/#events) endpoint - there is no separate endpoint to call here. When you fetch\nevents, the [common errors](/#errors) apply. Delivery failures are not returned to you;\nVanlo retries delivery (see [Webhooks](/#webhooks))."
    },
    {
      "id": "events",
      "title": "Events",
      "content": "# Events\n\nAn Event is created each time a significant action occurs in your account - for example, when a tracker status changes or a batch finishes processing. Events are the records that get delivered to your webhook endpoints.\n\nYou can use the Events API to list past events, which is useful for debugging webhook delivery or replaying missed events.\n\nFor details on the structure of event payloads delivered to webhooks, see [Webhook Events](/#event-object).\n\n## List Events\n\n```shell\ncurl -X GET https://www.vanlo.com/api/v1/events \\\n  -H 'Authorization: Bearer VANLO_API_KEY' \\\n  -d 'page_size=5'\n```\n\n```ruby\nrequire 'vanlo'\nVanlo.api_key = 'VANLO_API_KEY'\n\nVanlo::Event.all(page_size: 5)\n```\n\n```python\nimport vanlo\nvanlo.api_key = 'VANLO_API_KEY'\n\nvanlo.Event.all(page_size=5)\n```\n\n```php\nrequire_once('/path/to/lib/vanlo.php');\n\\Vanlo\\Vanlo::setApiKey('VANLO_API_KEY');\n\n$events = \\Vanlo\\Event::all(array('page_size' => 5));\n```\n\n```csharp\nusing Vanlo;\nClientManager.SetCurrent(\"VANLO_API_KEY\");\n\nvar listParams = new Dictionary<string, object>() {\n    { \"page_size\", 5 }\n};\n\nEventList eventList = Event.List(listParams);\n```\n\n> The above command returns JSON structured like this:\n\n```json\n{\n  \"events\": [\n    {\n      \"id\": \"evt_...\",\n      \"object\": \"Event\",\n      \"description\": \"tracker.updated\",\n      \"pending_urls\": [],\n      \"completed_urls\": [\"https://example.com/webhooks\"],\n      \"failed_urls\": [],\n      \"result\": {\n        \"id\": \"trk_...\",\n        \"object\": \"Tracker\",\n        \"status\": \"in_transit\",\n        \"carrier\": \"USPS\",\n        \"tracking_code\": \"9405500205903028777744\",\n        \"shipment_id\": \"shp_...\",\n        \"tracking_details\": [\n          {\n            \"object\": \"TrackingDetail\",\n            \"status\": \"in_transit\",\n            \"message\": \"Arrived at Sort Facility\",\n            \"datetime\": \"2020-04-13T13:58:52Z\",\n            \"source\": \"USPS\",\n            \"tracking_location\": {\n              \"zip\": \"60290\",\n              \"city\": \"Chicago\",\n              \"state\": \"IL\",\n              \"country\": \"US\"\n            }\n          }\n        ],\n        \"created_at\": \"2020-04-13T13:55:52Z\",\n        \"updated_at\": \"2020-04-13T13:58:52Z\"\n      },\n      \"created_at\": \"2020-04-13T14:00:27Z\",\n      \"updated_at\": \"2020-04-13T14:00:27Z\"\n    }\n  ],\n  \"has_more\": true\n}\n```\n\nThe Event List is a paginated list of all Event objects associated with the given API key. The `has_more` attribute indicates whether additional pages can be requested. The recommended way of paginating is to use either the `before_id` or `after_id` parameter to specify where the next page begins.\n\nThe `result` field contains the full object payload that was delivered to your webhooks. See [Webhook Events](/#event-object) for the structure of each event type.\n\n### HTTP Request\n\n`GET https://www.vanlo.com/api/v1/events`\n\n### List Events Request Parameters\n\nParameter | Type | Specification\n--------- | ----- | -----\nbefore_id | string | Return events created before this id\nafter_id | string | Return events created after this id\nstart_datetime | datetime | Only return events created after this timestamp\nend_datetime | datetime | Only return events created before this timestamp\npage_size | integer | Number of events to return per page (default 20, max 100)\n\n### Event Errors\n\nListing events is read-only and returns only the [common errors](/#errors) shared by every\nendpoint: authentication (401), an invalid filter or Accept header (400 / 406), rate\nlimiting (429), and server errors (500). There is no single-event lookup, so it does not\nreturn 404. See [Errors](/#errors) for the full list and how to handle each one."
    },
    {
      "id": "errors",
      "title": "Errors",
      "content": "# Errors\n\nThe Vanlo API uses standard HTTP status codes and returns a structured error body on\nevery failed request. The HTTP status tells you the broad class of problem; the\nmachine-readable `code` tells you exactly what went wrong.\n\n> The error response is structured like this:\n\n```json\n{\n  \"error\": {\n    \"code\": \"PARAMETER.REQUIRED\",\n    \"message\": \"Missing required parameter.\",\n    \"errors\": [\n      { \"field\": \"to_address\", \"message\": \"is required\" }\n    ]\n  }\n}\n```\n\nEvery error response has a single `error` object with these fields:\n\n- `error.code` - a stable, machine-readable string (for example `PARAMETER.REQUIRED`). Write your error handling against this value, not against the message text.\n- `error.message` - a human-readable description. The wording can change over time, so do not match on it.\n- `error.errors[]` - an array of `{ field, message }` objects. It is populated for 422 field validation so you can show the caller which fields were wrong.\n- `error.details` - an optional object with extra hints. It is present only when the endpoint sets it (for example the partial scan-form ids returned on a scan-form scoping error).\n\nThe envelope mirrors the EasyPost error shape (`error: { code, message, errors[] }`), so an\nEasyPost-compatible client can read Vanlo errors with little change. The `details` field is a\nVanlo-specific addition (for example the partial scan-form ids returned on a scan-form scoping\nerror).\n\nThe codes below are the common errors that any endpoint can return. Endpoint-specific codes -\nfor example `PAYMENT_REQUIRED` when buying a label, or `PROVIDER_503` on address verification -\nare documented in that endpoint's own Errors section.\n\n**Client errors (4xx)** - the request is the problem. Fix it, and retry only where noted.\n\nStatus | Code | Message | When it happens | How to handle\n------ | ---- | ------- | --------------- | -------------\n400 | `BAD_REQUEST` | Bad request, please check params and retry. | Malformed request the API cannot parse or route. | Fix the request, then retry.\n401 | `UNAUTHORIZED` | Unable to access the requested resource, authorization failed. | Missing or invalid API key. | Check the API key / Authorization header.\n403 | `FORBIDDEN` | Unable to access the requested resource. | The key lacks permission for this resource. | Do not retry; use an authorized key.\n403 | `IP_ADDRESS.FORBIDDEN` | The request could not be completed, the IP address is not authorized to use the API key. | Request from an IP not on the key's allowlist. | Call from an allowlisted IP.\n404 | `NOT_FOUND` | The requested resource could not be found. | Unknown id or wrong path. | Check the id; do not retry unchanged.\n406 | `NOT_ACCEPTABLE` | The resource identified by the request is only capable of generating response entities which have content characteristics not acceptable according to the accept headers sent in the request. | Accept header requests an unsupported representation. | Fix the Accept header.\n422 | `UNPROCESSABLE_ENTITY` | The request was understood, but cannot be processed. | Semantically invalid request. | Fix the input; do not retry unchanged.\n422 | `PARAMETER.FORBIDDEN` | The request could not be completed due to forbidden properties present in the parameters. | A forbidden field was sent. | Remove the forbidden field.\n422 | `INVALID_PARAMETERS` | The parameters are invalid. | One or more params invalid; see `error.errors[]`. | Fix the listed fields.\n422 | `PARAMETER.REQUIRED` | Missing required parameter. | A required field is absent; see `error.errors[]`. | Add the missing field.\n422 | `PARAMETER.INVALID_TYPE` | Wrong parameter type. | A field has the wrong type. | Send the correct type.\n429 | `CONFLICT` | You're trying to create or update the same entity twice at the same time. | Duplicate concurrent create/update. | Retry once after a short delay; make writes idempotent.\n429 | `LOCKED` | Another resource is creating at the moment. | A concurrent operation holds the lock (e.g. bursty scan-form). | Retry with backoff.\n\n**Server errors (5xx)** - not caused by your request, and usually safe to retry with backoff.\n\nStatus | Code | Message | When it happens | How to handle\n------ | ---- | ------- | --------------- | -------------\n500 | `INTERNAL_SERVER_ERROR` | We're sorry, something went wrong. If the problem persists please contact support. | Unexpected error on Vanlo's side. | Retry later; contact support if it persists.\n503 | `MAINTENANCE` | Service temporarily unavailable due to scheduled maintenance. | Scheduled maintenance window. | Retry later.\n504 | `TIMEOUT_EXCEEDED` | The request could not be completed because an internal timeout was exceeded. | Internal processing exceeded the timeout. | Retry with backoff.\n\n<aside class=\"notice\">\nPlease contact <a href=\"mailto:support@vanlo.com\">support@vanlo.com</a> for assistance with errors.\n</aside>"
    }
  ],
  "guides": [
    {
      "id": "address_verification",
      "title": "Address Verification Guide",
      "content": "# Address Verification Guide\n\nThis guide will teach you how to verify a shipping address with Vanlo before you spend money on a label.\n\nAddress verification checks an address against the carrier and postal databases. It confirms the address really exists and can receive mail, fixes small formatting problems (like a missing suite line or the wrong ZIP+4), and tells you when something is wrong. Verifying early means fewer failed deliveries, fewer returns, and fewer address-correction surcharges from the carrier.\n\nYou ask for verification when you **create** an <a href=\"/#addresses\">Address</a>, by sending a `verify` (or `verify_strict`) flag alongside the address. You can verify any address you collect from a customer - a checkout form, an import, or a bulk upload.\n\n<p class=\"guides-label\">Before You Start</p>\n\n<div class=\"guides-info\">\n  <p><a href=\"https://dashboard.vanlo.com/\">Log in to an existing account.</a></p>\n  <p>Grab one of our <a href=\"https://github.com/VanloCorp\">official client libraries</a>.</p>\n  <p>If you haven't run through our <a href=\"#\" data-guide-switch=\"getting-started\">Getting Started Guide</a>, definitely do that before moving on to this one.</p>\n</div>\n\n<div class=\"guides-related-cards\">\n  <a class=\"guides-related-card\" href=\"#\" data-guide-switch=\"getting-started\">\n    <div class=\"guides-related-card__text\">\n      <span class=\"guides-related-card__label\">RELATED LINK</span>\n      <span class=\"guides-related-card__title\">Getting Started Guide</span>\n    </div>\n    <svg class=\"guides-related-card__icon\" width=\"17\" height=\"17\" viewBox=\"0 0 17 17\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\"><path d=\"M8.25 11.25L11.25 8.25M11.25 8.25L8.25 5.25M11.25 8.25H5.25M15.75 8.25C15.75 12.3921 12.3921 15.75 8.25 15.75C4.10786 15.75 0.75 12.3921 0.75 8.25C0.75 4.10786 4.10786 0.75 8.25 0.75C12.3921 0.75 15.75 4.10786 15.75 8.25Z\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/></svg>\n  </a>\n</div>\n\n\n## Best-Effort and Strict Verification\n\nVanlo gives you two ways to verify, and they differ in one important way: what happens when the address **cannot** be verified.\n\nBest-effort verification (`verify`) always saves the address. If the address can't be matched, you still get the saved <a href=\"/#addresses\">Address</a> back, and the problems are reported inside the `verifications` object on the response. You decide what to do next - store it anyway, show the customer a warning, or ask them to fix it. Pick this when you want to keep every address and review issues yourself.\n\nStrict verification (`verify_strict`) only saves a clean address. If the address can't be verified, the request is rejected with `422 CARRIER_REJECTED_ADDRESS` and **nothing is saved**. Pick this when only a deliverable address should ever enter your system.\n\nBoth flags take an array of verification types. Today the supported type is `\"delivery\"`, which checks that the address is real and deliverable. Send the flag at the top level of the request, next to the `address` object - not inside it.\n\n\n## Step 1: Verify an Address While You Create It\n\nTo verify an address as you create it, add `verify: [\"delivery\"]` to the create request. The address is saved either way, and the result of the check comes back in the `verifications` object.\n\nHere is how to create an address and run a best-effort verification:\n\n\n### Create and Verify an Address\n\n```shell\ncurl -X POST https://www.vanlo.com/api/v1/addresses \\\n  -H 'Authorization: Bearer VANLO_API_KEY' \\\n  -H 'Content-Type: application/json' \\\n  -d '{\n  \"address\": {\n    \"company\": \"VANLO\",\n    \"street1\": \"123 MONTGOMERY ST\",\n    \"street2\": \"STE 400\",\n    \"city\": \"SAN FRANCISCO\",\n    \"state\": \"CA\",\n    \"zip\": \"94104\",\n    \"country\": \"US\",\n    \"phone\": \"4151234567\"\n  },\n  \"verify\": [\"delivery\"]\n}'\n```\n\n```ruby\nrequire 'vanlo'\nVanlo.api_key = 'VANLO_API_KEY'\n\nVanlo::Address.create(\n  verify: ['delivery'],\n  company: 'VANLO',\n  street1: '123 MONTGOMERY ST',\n  street2: 'STE 400',\n  city: 'SAN FRANCISCO',\n  state: 'CA',\n  zip: '94104',\n  country: 'US',\n  phone: '4151234567'\n)\n```\n\n```python\nimport vanlo\nvanlo.api_key = 'VANLO_API_KEY'\n\nvanlo.Address.create(\n  verify=['delivery'],\n  company='VANLO',\n  street1='123 MONTGOMERY ST',\n  street2='STE 400',\n  city='SAN FRANCISCO',\n  state='CA',\n  zip='94104',\n  country='US',\n  phone='4151234567'\n)\n```\n\n```php\nrequire_once(\"/path/to/lib/vanlo.php\");\n\\Vanlo\\Vanlo::setApiKey('VANLO_API_KEY');\n\n\\Vanlo\\Address::create(array(\n  \"verify\"  => array(\"delivery\"),\n  \"company\" => \"VANLO\",\n  \"street1\" => \"123 MONTGOMERY ST\",\n  \"street2\" => \"STE 400\",\n  \"city\"    => \"SAN FRANCISCO\",\n  \"state\"   => \"CA\",\n  \"zip\"     => \"94104\",\n  \"country\" => \"US\",\n  \"phone\"   => \"4151234567\"\n));\n```\n\n```csharp\nusing Vanlo;\nClientManager.SetCurrent(\"VANLO_API_KEY\");\n\nAddress address = Address.Create(\n    new Dictionary<string, object>() {\n        { \"verify\",  new List<string>() { \"delivery\" } },\n        { \"company\", \"VANLO\" },\n        { \"street1\", \"123 MONTGOMERY ST\" },\n        { \"street2\", \"STE 400\" },\n        { \"city\",    \"SAN FRANCISCO\" },\n        { \"state\",   \"CA\" },\n        { \"zip\",     \"94104\" },\n        { \"country\", \"US\" },\n        { \"phone\",   \"415-123-4567\" }\n    }\n);\n```\n\nWhen the address verifies, you get back the saved <a href=\"/#addresses\">Address</a> with `verifications.delivery.success` set to `true`. Any fields the carrier corrected (for example a normalized street name or a full ZIP+4) are already applied to the address you get back.\n\n\n### Clean Verification Response\n\n```json\n{\n  \"id\": \"adr_...\",\n  \"object\": \"Address\",\n  \"created_at\": \"2019-09-06T12:01:52.503Z\",\n  \"updated_at\": \"2019-09-06T12:01:52.503Z\",\n  \"name\": null,\n  \"company\": \"VANLO\",\n  \"street1\": \"123 MONTGOMERY ST\",\n  \"street2\": \"STE 400\",\n  \"city\": \"SAN FRANCISCO\",\n  \"state\": \"CA\",\n  \"zip\": \"94104\",\n  \"country\": \"US\",\n  \"phone\": \"4151234567\",\n  \"email\": null,\n  \"residential\": false,\n  \"verifications\": {\n    \"delivery\": {\n      \"success\": true,\n      \"details\": {\n        \"latitude\": 37.79066,\n        \"longitude\": -122.40103,\n        \"time_zone\": \"America/Los_Angeles\"\n      },\n      \"errors\": null\n    },\n    \"verify\": [\"delivery\"]\n  }\n}\n```\n\nBecause this is best-effort, the address is saved even when it cannot be verified. In that case you still get a `201` with the saved address, but `verifications.delivery.success` is `false` and `errors` lists what went wrong. Each error has a `code`, the `field` it applies to, a human-readable `message`, and an optional `suggestion`. Read these to decide whether to keep the address, warn the customer, or ask them to fix it.\n\n\n### Verification With Problems Response\n\n```json\n{\n  \"id\": \"adr_...\",\n  \"object\": \"Address\",\n  \"created_at\": \"2019-09-06T12:01:52.503Z\",\n  \"updated_at\": \"2019-09-06T12:01:52.503Z\",\n  \"name\": null,\n  \"company\": \"VANLO\",\n  \"street1\": \"MONTGOMERY ST\",\n  \"street2\": \"STE 400\",\n  \"city\": \"SAN FRANCISCO\",\n  \"state\": \"CA\",\n  \"zip\": \"94104\",\n  \"country\": \"US\",\n  \"phone\": \"4151234567\",\n  \"email\": null,\n  \"residential\": false,\n  \"verifications\": {\n    \"delivery\": {\n      \"success\": false,\n      \"details\": {},\n      \"errors\": [\n        {\n          \"code\": \"E.ADDRESS.NOT_FOUND\",\n          \"field\": \"address\",\n          \"message\": \"Address not found\",\n          \"suggestion\": null\n        },\n        {\n          \"code\": \"E.HOUSE_NUMBER.MISSING\",\n          \"field\": \"street1\",\n          \"message\": \"House number is missing\",\n          \"suggestion\": null\n        }\n      ]\n    },\n    \"verify\": [\"delivery\"]\n  }\n}\n```\n\n<div class=\"guides-related-cards\">\n  <a class=\"guides-related-card\" href=\"/#address-object\">\n    <div class=\"guides-related-card__text\">\n      <span class=\"guides-related-card__label\">RELATED LINK</span>\n      <span class=\"guides-related-card__title\">Address Object</span>\n    </div>\n    <svg class=\"guides-related-card__icon\" width=\"17\" height=\"17\" viewBox=\"0 0 17 17\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\"><path d=\"M8.25 11.25L11.25 8.25M11.25 8.25L8.25 5.25M11.25 8.25H5.25M15.75 8.25C15.75 12.3921 12.3921 15.75 8.25 15.75C4.10786 15.75 0.75 12.3921 0.75 8.25C0.75 4.10786 4.10786 0.75 8.25 0.75C12.3921 0.75 15.75 4.10786 15.75 8.25Z\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/></svg>\n  </a>\n</div>\n\n\n## Step 2: Require a Verified Address\n\nWhen you only want deliverable addresses in your system, use `verify_strict` instead of `verify`. Send it the same way - an array at the top level of the request.\n\nThe request below is identical to Step 1, except the flag is `verify_strict`:\n\n\n### Create With Strict Verification\n\n```shell\ncurl -X POST https://www.vanlo.com/api/v1/addresses \\\n  -H 'Authorization: Bearer VANLO_API_KEY' \\\n  -H 'Content-Type: application/json' \\\n  -d '{\n  \"address\": {\n    \"company\": \"VANLO\",\n    \"street1\": \"123 MONTGOMERY ST\",\n    \"street2\": \"STE 400\",\n    \"city\": \"SAN FRANCISCO\",\n    \"state\": \"CA\",\n    \"zip\": \"94104\",\n    \"country\": \"US\",\n    \"phone\": \"4151234567\"\n  },\n  \"verify_strict\": [\"delivery\"]\n}'\n```\n\n```ruby\nrequire 'vanlo'\nVanlo.api_key = 'VANLO_API_KEY'\n\nVanlo::Address.create(\n  verify_strict: ['delivery'],\n  company: 'VANLO',\n  street1: '123 MONTGOMERY ST',\n  street2: 'STE 400',\n  city: 'SAN FRANCISCO',\n  state: 'CA',\n  zip: '94104',\n  country: 'US',\n  phone: '4151234567'\n)\n```\n\n```python\nimport vanlo\nvanlo.api_key = 'VANLO_API_KEY'\n\nvanlo.Address.create(\n  verify_strict=['delivery'],\n  company='VANLO',\n  street1='123 MONTGOMERY ST',\n  street2='STE 400',\n  city='SAN FRANCISCO',\n  state='CA',\n  zip='94104',\n  country='US',\n  phone='4151234567'\n)\n```\n\n```php\nrequire_once(\"/path/to/lib/vanlo.php\");\n\\Vanlo\\Vanlo::setApiKey('VANLO_API_KEY');\n\n\\Vanlo\\Address::create(array(\n  \"verify_strict\" => array(\"delivery\"),\n  \"company\"       => \"VANLO\",\n  \"street1\"       => \"123 MONTGOMERY ST\",\n  \"street2\"       => \"STE 400\",\n  \"city\"          => \"SAN FRANCISCO\",\n  \"state\"         => \"CA\",\n  \"zip\"           => \"94104\",\n  \"country\"       => \"US\",\n  \"phone\"         => \"4151234567\"\n));\n```\n\n```csharp\nusing Vanlo;\nClientManager.SetCurrent(\"VANLO_API_KEY\");\n\nAddress address = Address.Create(\n    new Dictionary<string, object>() {\n        { \"verify_strict\", new List<string>() { \"delivery\" } },\n        { \"company\", \"VANLO\" },\n        { \"street1\", \"123 MONTGOMERY ST\" },\n        { \"street2\", \"STE 400\" },\n        { \"city\",    \"SAN FRANCISCO\" },\n        { \"state\",   \"CA\" },\n        { \"zip\",     \"94104\" },\n        { \"country\", \"US\" },\n        { \"phone\",   \"415-123-4567\" }\n    }\n);\n```\n\nIf the address verifies, you get the same `201` with the saved, corrected address you saw in Step 1. If it cannot be verified, the request is rejected with `422 CARRIER_REJECTED_ADDRESS`, no address is saved, and the problems are returned in `error.errors[]` as `{ field, message }` pairs.\n\n\n### Strict Verification Rejected Response\n\n```json\n{\n  \"error\": {\n    \"code\": \"CARRIER_REJECTED_ADDRESS\",\n    \"message\": \"The address could not be verified.\",\n    \"errors\": [\n      { \"field\": \"address\", \"message\": \"Address not found\" },\n      { \"field\": \"street1\", \"message\": \"House number is missing\" }\n    ]\n  }\n}\n```\n\n\n## Step 3: Handle Verification Failures\n\nThere are two failures to plan for. Handle them based on the `error.code`, not the message text - the wording can change, but the code is stable.\n\n`422 CARRIER_REJECTED_ADDRESS` means a strict verification could not confirm the address. Read `error.errors[]` to see which fields are wrong, correct the address (often a missing house number, a wrong ZIP, or a typo in the street), and submit the create request again. Do not retry the same address unchanged - it will be rejected the same way. If you are collecting the address from a customer, show them the messages so they can fix their own entry.\n\n`503 PROVIDER_503` means the verification provider did not respond. This is a temporary problem on the provider's side, not a problem with your address. Retry the request after a short wait (back off and try again). If it keeps happening, contact <a href=\"mailto:support@vanlo.com\">support@vanlo.com</a>.\n\n\n### Provider Unavailable Response\n\n```json\n{\n  \"error\": {\n    \"code\": \"PROVIDER_503\",\n    \"message\": \"No response from service provider, please try again or contact support.\",\n    \"errors\": []\n  }\n}\n```\n\nFor the full list of error codes shared by every endpoint, see our <a href=\"/#errors\">Errors</a> reference. Once your addresses are verified, head to the <a href=\"#\" data-guide-switch=\"getting-started\">Getting Started Guide</a> to create a shipment and buy a label.\n\n\n<div class=\"guides-related-cards\">\n  <a class=\"guides-related-card\" href=\"/#address-object\">\n    <div class=\"guides-related-card__text\">\n      <span class=\"guides-related-card__label\">RELATED LINK</span>\n      <span class=\"guides-related-card__title\">Address Object</span>\n    </div>\n    <svg class=\"guides-related-card__icon\" width=\"17\" height=\"17\" viewBox=\"0 0 17 17\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\"><path d=\"M8.25 11.25L11.25 8.25M11.25 8.25L8.25 5.25M11.25 8.25H5.25M15.75 8.25C15.75 12.3921 12.3921 15.75 8.25 15.75C4.10786 15.75 0.75 12.3921 0.75 8.25C0.75 4.10786 4.10786 0.75 8.25 0.75C12.3921 0.75 15.75 4.10786 15.75 8.25Z\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/></svg>\n  </a>\n  <a class=\"guides-related-card\" href=\"#\" data-guide-switch=\"getting-started\">\n    <div class=\"guides-related-card__text\">\n      <span class=\"guides-related-card__label\">RELATED LINK</span>\n      <span class=\"guides-related-card__title\">Getting Started Guide</span>\n    </div>\n    <svg class=\"guides-related-card__icon\" width=\"17\" height=\"17\" viewBox=\"0 0 17 17\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\"><path d=\"M8.25 11.25L11.25 8.25M11.25 8.25L8.25 5.25M11.25 8.25H5.25M15.75 8.25C15.75 12.3921 12.3921 15.75 8.25 15.75C4.10786 15.75 0.75 12.3921 0.75 8.25C0.75 4.10786 4.10786 0.75 8.25 0.75C12.3921 0.75 15.75 4.10786 15.75 8.25Z\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/></svg>\n  </a>\n</div>"
    },
    {
      "id": "batch",
      "title": "Batch Guide",
      "content": "# Batch Guide\n\nThis guide will teach you how to use Batches to create and purchase shipping labels. Batches make it simpler to create and purchase many shipping labels in just a few API calls. Even better, we group all the labels in a batch into a single file that you can download.\n\nIn this example, we will be shipping four T-Shirts to four very lucky Vanlo fans. Batches can support up to 10,000 shipments per batch.\n\n<p class=\"guides-label\">Before You Start</p>\n\n<div class=\"guides-info\">\n  <p><a href=\"https://dashboard.vanlo.com/\">Log in to an existing account.</a> Or email <a href=\"mailto:support@vanlo.com\">support@vanlo.com</a></p>\n  <p><a href=\"https://dashboard.vanlo.com/webhooks\">Set up your webhook URLs</a> for Test Mode and Production Mode.</p>\n  <p>Grab one of our <a href=\"https://github.com/VanloCorp\">official client libraries</a>.</p>\n  <p>If you haven't run through our <a href=\"#\" data-guide-switch=\"getting-started\">Getting Started Guide</a>, definitely do that before moving on to this one.</p>\n</div>\n\n<div class=\"guides-related-cards\">\n  <a class=\"guides-related-card\" href=\"#\" data-guide-switch=\"getting-started\">\n    <div class=\"guides-related-card__text\">\n      <span class=\"guides-related-card__label\">RELATED LINK</span>\n      <span class=\"guides-related-card__title\">Getting Started Guide</span>\n    </div>\n    <svg class=\"guides-related-card__icon\" width=\"17\" height=\"17\" viewBox=\"0 0 17 17\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\"><path d=\"M8.25 11.25L11.25 8.25M11.25 8.25L8.25 5.25M11.25 8.25H5.25M15.75 8.25C15.75 12.3921 12.3921 15.75 8.25 15.75C4.10786 15.75 0.75 12.3921 0.75 8.25C0.75 4.10786 4.10786 0.75 8.25 0.75C12.3921 0.75 15.75 4.10786 15.75 8.25Z\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/></svg>\n  </a>\n</div>\n\n\n## Step 1: Creating a Batch of Shipments\n\nA Batch is a collection of Shipments that you purchase and generate labels for together. When creating a Batch, you can either:\n\nCreate and purchase the Shipment objects ahead of time and pass Shipment IDs.\n\nPass us the information needed to create <a href=\"/#address-object\">Address</a>, <a href=\"/#parcel-object\">Parcel</a>, and <a href=\"/#shipment-object\">Shipment</a> objects as well as tell us which Carrier and Service you want to use for that <a href=\"/#shipment-object\">Shipment</a>.\n\nWhen you create a Batch, the Shipments within the Batch are created asynchronously. When you first POST to create the Batch, we will send a webhook event to your app that the Batch object is being created. (\"status\":\"creating\"). Due to the webhook update being asynchronous, you should carefully consider the implications of using Batches instead of multiple Shipment requests.\n\nOnce all shipments are created, we will send another webhook to your application telling you it is complete (\"status\":\"created\"). If there are any errors for the Batch object, we will tell you about them in the webhook (\"state\":\"creation_failed\"). <a href=\"#step-2-adding-and-removing-shipments-from-a-batch\">Step 2</a> of this tutorial will show you how to fix any errors that arise. There is more information on webhooks later in the tutorial.\n\n\n### Creating a Batch\n\n```shell\ncurl -X POST https://www.vanlo.com/api/v1/batches \\\n  -H 'Authorization: Bearer VANLO_API_KEY' \\\n  -H 'Content-Type: application/json' \\\n  -d '{\n    \"batch\": {\n      \"shipments\": [\n        {\n          \"id\": \"shp_...\"\n        },\n        {\n          \"id\": \"shp_...\"\n        }\n      ]\n    }\n  }'\n```\n\n```ruby\nrequire 'vanlo'\nVanlo.api_key = 'VANLO_API_KEY'\n\nVanlo::Batch.create(\n  shipments: [\n    { id: \"shp_...\" },\n    { id: \"shp_...\" }\n  ]\n)\n```\n\n```python\nimport vanlo\nvanlo.api_key = 'VANLO_API_KEY'\n\nvanlo.Batch.create(\n  shipments=[\n    { \"id\": \"shp_...\" },\n    { \"id\": \"shp_...\" }\n  ]\n)\n```\n\n```php\nrequire_once('/path/to/lib/vanlo.php');\n\\Vanlo\\Vanlo::setApiKey('VANLO_API_KEY');\n\n$batch = \\Vanlo\\Batch::create(array(\n  'shipments' => array(\n    array('id' => 'shp_...'),\n    array('id' => 'shp_...')\n  )\n));\n```\n\n```csharp\nusing Vanlo;\nClientManager.SetCurrent(\"VANLO_API_KEY\");\n\nBatch batch = Batch.Create(new Dictionary<string, object>() {\n    { \"shipments\", new List<Dictionary<string, object>>() {\n        new Dictionary<string, object>() { { \"id\", \"shp_...\" } },\n        new Dictionary<string, object>() { { \"id\", \"shp_...\" } }\n    } }\n});\n```\n\n\n### Batch JSON Response\n\n```json\n{\n  \"id\": \"batch_35638a505b00489da8feea4209b66c01\",\n  \"object\": \"Batch\",\n  \"mode\": \"test\",\n  \"state\": \"creating\",\n  \"num_shipments\": 1,\n  \"reference\": null,\n  \"created_at\": \"2022-10-17T17:16:05Z\",\n  \"updated_at\": \"2022-10-17T17:16:05Z\",\n  \"scan_form\": null,\n  \"shipments\": [],\n  \"status\": {\n    \"created\": 0,\n    \"queued_for_purchase\": 0,\n    \"creation_failed\": 0,\n    \"postage_purchased\": 0,\n    \"postage_purchase_failed\": 0\n  },\n  \"pickup\": null,\n  \"label_url\": null\n}\n```\n\n<div class=\"guides-related-cards\">\n  <a class=\"guides-related-card\" href=\"#batch-object\">\n    <div class=\"guides-related-card__text\">\n      <span class=\"guides-related-card__label\">RELATED LINK</span>\n      <span class=\"guides-related-card__title\">Batch Object</span>\n    </div>\n    <svg class=\"guides-related-card__icon\" width=\"17\" height=\"17\" viewBox=\"0 0 17 17\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\"><path d=\"M8.25 11.25L11.25 8.25M11.25 8.25L8.25 5.25M11.25 8.25H5.25M15.75 8.25C15.75 12.3921 12.3921 15.75 8.25 15.75C4.10786 15.75 0.75 12.3921 0.75 8.25C0.75 4.10786 4.10786 0.75 8.25 0.75C12.3921 0.75 15.75 4.10786 15.75 8.25Z\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/></svg>\n  </a>\n</div>\n\n\n## Step 2: Adding and Removing Shipments from a Batch\n\nAfter you create a Batch, you can still add Shipments to it. To add Shipments to a Batch, you need to create and purchase the Shipment object ahead of time and then add it to your already existing Batch.\n\nHere is an example:\n\n\n### Adding Shipments\n\n```shell\ncurl -X POST https://www.vanlo.com/api/v1/batches/batch_.../add_shipments \\\n  -H 'Authorization: Bearer VANLO_API_KEY' \\\n  -H 'Content-Type: application/json' \\\n  -d '{\n    \"shipments\": [\n      {\n        \"id\": \"shp_...\"\n      },\n      {\n        \"id\": \"shp_...\"\n      }\n    ]\n  }'\n```\n\n```ruby\nrequire 'vanlo'\nVanlo.api_key = 'VANLO_API_KEY'\n\nbatch = Vanlo::Batch.retrieve('batch_...')\nbatch.add_shipments(\n  shipments: [\n    { id: \"shp_...\" },\n    { id: \"shp_...\" }\n  ]\n)\n```\n\n```python\nimport vanlo\nvanlo.api_key = 'VANLO_API_KEY'\n\nbatch = vanlo.Batch.retrieve('batch_...')\nbatch.add_shipments(\n  shipments=[\n    { \"id\": \"shp_...\" },\n    { \"id\": \"shp_...\" }\n  ]\n)\n```\n\n```php\nrequire_once('/path/to/lib/vanlo.php');\n\\Vanlo\\Vanlo::setApiKey('VANLO_API_KEY');\n\n$batch = \\Vanlo\\Batch::retrieve('batch_...');\n$batch->add_shipments(array(\n  'shipments' => array(\n    array('id' => 'shp_...'),\n    array('id' => 'shp_...')\n  )\n));\n```\n\n```csharp\nusing Vanlo;\nClientManager.SetCurrent(\"VANLO_API_KEY\");\n\nBatch batch = Batch.Retrieve(\"batch_...\");\nbatch.AddShipments(new Dictionary<string, object>() {\n    { \"shipments\", new List<Dictionary<string, object>>() {\n        new Dictionary<string, object>() { { \"id\", \"shp_...\" } },\n        new Dictionary<string, object>() { { \"id\", \"shp_...\" } }\n    } }\n});\n```\n\n\n### Parcel Response\n\n```json\n{\n  \"id\": \"batch_5781439e197447ac850dc0796e9e8e76\",\n  \"object\": \"Batch\",\n  \"mode\": \"test\",\n  \"state\": \"created\",\n  \"num_shipments\": 1,\n  \"reference\": null,\n  \"created_at\": \"2022-10-17T17:16:27Z\",\n  \"updated_at\": \"2022-10-17T17:16:27Z\",\n  \"scan_form\": null,\n  \"shipments\": [\n    {\n      \"batch_status\": \"postage_purchased\",\n      \"batch_message\": null,\n      \"reference\": null,\n      \"tracking_code\": \"9405500106068143632665\",\n      \"id\": \"shp_d8a3856984f84c78b6b0da741f9fea7d\"\n    }\n  ],\n  \"status\": {\n    \"created\": 0,\n    \"queued_for_purchase\": 0,\n    \"creation_failed\": 0,\n    \"postage_purchased\": 1,\n    \"postage_purchase_failed\": 0\n  },\n  \"pickup\": null,\n  \"label_url\": null\n}\n```\n\n<div class=\"guides-related-cards\">\n  <a class=\"guides-related-card\" href=\"#batch-object\">\n    <div class=\"guides-related-card__text\">\n      <span class=\"guides-related-card__label\">RELATED LINK</span>\n      <span class=\"guides-related-card__title\">Batch Object</span>\n    </div>\n    <svg class=\"guides-related-card__icon\" width=\"17\" height=\"17\" viewBox=\"0 0 17 17\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\"><path d=\"M8.25 11.25L11.25 8.25M11.25 8.25L8.25 5.25M11.25 8.25H5.25M15.75 8.25C15.75 12.3921 12.3921 15.75 8.25 15.75C4.10786 15.75 0.75 12.3921 0.75 8.25C0.75 4.10786 4.10786 0.75 8.25 0.75C12.3921 0.75 15.75 4.10786 15.75 8.25Z\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/></svg>\n  </a>\n</div>\n\nThere may be times when you need to remove a Shipment from a Batch. You can do that too. For example, a particular Shipment may have an invalid address but you may still want to continue on with the rest of the Shipments.\n\nYou can easily remove a Shipment from a Batch with the following code:\n\n\n### Removing Shipments\n\n```shell\ncurl -X POST https://www.vanlo.com/api/v1/batches/batch_.../remove_shipments \\\n  -H 'Authorization: Bearer VANLO_API_KEY' \\\n  -H 'Content-Type: application/json' \\\n  -d '{\n    \"shipments\": [\n      {\n        \"id\": \"shp_...\"\n      }\n    ]\n  }'\n```\n\n```ruby\nrequire 'vanlo'\nVanlo.api_key = 'VANLO_API_KEY'\n\nbatch = Vanlo::Batch.retrieve('batch_...')\nbatch.remove_shipments(\n  shipments: [\n    { id: \"shp_...\" }\n  ]\n)\n```\n\n```python\nimport vanlo\nvanlo.api_key = 'VANLO_API_KEY'\n\nbatch = vanlo.Batch.retrieve('batch_...')\nbatch.remove_shipments(\n  shipments=[\n    { \"id\": \"shp_...\" }\n  ]\n)\n```\n\n```php\nrequire_once('/path/to/lib/vanlo.php');\n\\Vanlo\\Vanlo::setApiKey('VANLO_API_KEY');\n\n$batch = \\Vanlo\\Batch::retrieve('batch_...');\n$batch->remove_shipments(array(\n  'shipments' => array(\n    array('id' => 'shp_...')\n  )\n));\n```\n\n```csharp\nusing Vanlo;\nClientManager.SetCurrent(\"VANLO_API_KEY\");\n\nBatch batch = Batch.Retrieve(\"batch_...\");\nbatch.RemoveShipments(new Dictionary<string, object>() {\n    { \"shipments\", new List<Dictionary<string, object>>() {\n        new Dictionary<string, object>() { { \"id\", \"shp_...\" } }\n    } }\n});\n```\n\n\n### Removing Shipment JSON Response\n\n```json\n{\n  \"id\": \"batch_1e08a75fe682484caf66f8382d5b6a11\",\n  \"object\": \"Batch\",\n  \"mode\": \"test\",\n  \"state\": \"purchased\",\n  \"num_shipments\": 0,\n  \"reference\": null,\n  \"created_at\": \"2022-10-17T17:16:29Z\",\n  \"updated_at\": \"2022-10-17T17:16:30Z\",\n  \"scan_form\": null,\n  \"shipments\": [],\n  \"status\": {\n    \"created\": 0,\n    \"queued_for_purchase\": 0,\n    \"creation_failed\": 0,\n    \"postage_purchased\": 0,\n    \"postage_purchase_failed\": 0\n  },\n  \"pickup\": null,\n  \"label_url\": null\n}\n```\n\n\n## Step 3: Creating and Purchasing Shipping labels for a Batch\n\nThe next step is to purchase and create all labels for the shipments in your batch. All you need to do is issue a buy on the particular Batch object you're ready to buy. When you buy the batch, we kick off an asynchronous process to create all the labels you need.\n\nThe initial response from buying a Batch will not have the URL of a label. Because we support up to 10,000 shipments in a Batch, it takes time to create all the labels.\n\nOnce we've purchased and created all the labels for a batch, we'll send a webhook to your application letting you know that it has completed. When completed, the \"state\" of the Batch object will be \"purchased\". If there are any errors, the state of the Batch object will be \"purchase_failed\". You will need to fix or remove any of the shipments that failed before proceeding to the next step of creating the Batch Label.\n\nHere's a code example of buying a Batch:\n\n\n### Buying a Batch\n\n```shell\ncurl -X POST https://www.vanlo.com/api/v1/batches/batch_.../buy \\\n  -H 'Authorization: Bearer VANLO_API_KEY'\n```\n\n```ruby\nrequire 'vanlo'\nVanlo.api_key = 'VANLO_API_KEY'\n\nbatch = Vanlo::Batch.retrieve('batch_...')\nbatch.buy\n```\n\n```python\nimport vanlo\nvanlo.api_key = 'VANLO_API_KEY'\n\nbatch = vanlo.Batch.retrieve('batch_...')\nbatch.buy()\n```\n\n```php\nrequire_once('/path/to/lib/vanlo.php');\n\\Vanlo\\Vanlo::setApiKey('VANLO_API_KEY');\n\n$batch = \\Vanlo\\Batch::retrieve('batch_...');\n$batch->buy();\n```\n\n```csharp\nusing Vanlo;\nClientManager.SetCurrent(\"VANLO_API_KEY\");\n\nBatch batch = Batch.Retrieve(\"batch_...\");\nbatch.Buy();\n```\n\n\n### Printing Results\n\n```json\n{\n  \"id\": \"batch_8323a3f140b543fc90a7acdb01912ae7\",\n  \"object\": \"Batch\",\n  \"mode\": \"test\",\n  \"state\": \"created\",\n  \"num_shipments\": 1,\n  \"reference\": null,\n  \"created_at\": \"2022-10-17T17:16:08Z\",\n  \"updated_at\": \"2022-10-17T17:16:08Z\",\n  \"scan_form\": null,\n  \"shipments\": [\n    {\n      \"batch_status\": \"queued_for_purchase\",\n      \"batch_message\": null,\n      \"reference\": null,\n      \"tracking_code\": null,\n      \"id\": \"shp_5188f6665c5b41569530ba4698225e3e\"\n    }\n  ],\n  \"status\": {\n    \"created\": 1,\n    \"queued_for_purchase\": 0,\n    \"creation_failed\": 0,\n    \"postage_purchased\": 0,\n    \"postage_purchase_failed\": 0\n  },\n  \"pickup\": null,\n  \"label_url\": null\n}\n```\n\n\n## Step 4: Creating a Batch Label\n\nOnce you have received the webhook that your Batch has been purchased, the final step is to create and retrieve all the shipping labels in a single Batch Label. All the labels for the Batch will be in a single file that you download. A Batch Label can be retrieved as a single `pdf`, a single `zpl`, or a `zip` archive containing each individual label file. The `file_format` value must be lowercase.\n\n\n### Creating Batch Label\n\n```shell\ncurl -X POST https://www.vanlo.com/api/v1/batches/batch_.../label \\\n  -H 'Authorization: Bearer VANLO_API_KEY' \\\n  -H 'Content-Type: application/json' \\\n  -d '{\n    \"file_format\": \"pdf\"\n  }'\n```\n\n```ruby\nrequire 'vanlo'\nVanlo.api_key = 'VANLO_API_KEY'\n\nbatch = Vanlo::Batch.retrieve('batch_...')\nbatch.label(file_format: 'pdf')\n```\n\n```python\nimport vanlo\nvanlo.api_key = 'VANLO_API_KEY'\n\nbatch = vanlo.Batch.retrieve('batch_...')\nbatch.label(file_format='pdf')\n```\n\n```php\nrequire_once('/path/to/lib/vanlo.php');\n\\Vanlo\\Vanlo::setApiKey('VANLO_API_KEY');\n\n$batch = \\Vanlo\\Batch::retrieve('batch_...');\n$batch->label(array('file_format' => 'pdf'));\n```\n\n```csharp\nusing Vanlo;\nClientManager.SetCurrent(\"VANLO_API_KEY\");\n\nBatch batch = Batch.Retrieve(\"batch_...\");\nbatch.GenerateLabel(\"pdf\");\n```\n\n\n### Creating Batch Label Response\n\n```json\n{\n  \"id\": \"batch_6e7c7e6f280b4be396463a49857f5bed\",\n  \"object\": \"Batch\",\n  \"mode\": \"test\",\n  \"state\": \"label_generating\",\n  \"num_shipments\": 1,\n  \"reference\": null,\n  \"created_at\": \"2022-10-17T17:16:31Z\",\n  \"updated_at\": \"2022-10-17T17:16:41Z\",\n  \"scan_form\": null,\n  \"shipments\": [\n    {\n      \"batch_status\": \"postage_purchased\",\n      \"batch_message\": null,\n      \"reference\": null,\n      \"tracking_code\": \"9405500106068143632733\",\n      \"id\": \"shp_e2140993b93146b2bc2d9323811947b9\"\n    }\n  ],\n  \"status\": {\n    \"created\": 0,\n    \"queued_for_purchase\": 0,\n    \"creation_failed\": 0,\n    \"postage_purchased\": 1,\n    \"postage_purchase_failed\": 0\n  },\n  \"pickup\": null,\n  \"label_url\": null\n}\n```\n\nYou can only get shipping labels for a Batch if all Shipments in the 'postage_purchased' status. If any of your purchases are failed, you should just remove that Shipment from the Batch during the previous step.\n\nIf the Batch Label fails to create, the Batch object will return back to \"purchased\" state.\n\n\n## Step 5: Using Webhooks for a Batch\n\nWhen using Batches, webhooks are useful at three points:\n\nCreating a Batch (<a href=\"#step-1-creating-a-batch-of-shipments\">Step 1</a> in the tutorial)\n\nPurchasing and creating labels for a Batch (<a href=\"#step-3-creating-and-purchasing-shipping-labels-for-a-batch\">Step 3</a> in the tutorial)\n\nCreating and retrieving a Batch label (<a href=\"#step-4-creating-a-batch-label\">Step 4</a> in the tutorial)\n\n<a href=\"/#webhook-object\">Webhooks</a> are required because these processes are completed asynchronously. You will receive a webhook both on the initial POST to Vanlo and once the given action has reached a final state. If there are any errors, we will pass them back in the associated webhook.\n\nWhen evaluating Events that hit your webhook URL, you'll know it is a Batch event because the `object` field is `\"Event\"` and the `description` is `\"batch.created\"` or `\"batch.updated\"`. As part of this <a href=\"/#event-object\">Event</a>, we will also pass you back the Batch object. The value of the Event's `result` attribute will contain the Batch object. Check our <a href=\"#\" data-guide-switch=\"webhooks\">Webhooks Guide</a> for additional information on using webhooks.\n\nWe recommend you evaluate the \"state\" of the Batch object to check if your Batch was successfully processed. If there is an error (eg \"creation failed\" or \"purchase_failed\"), you can then inspect the individual Shipment objects we return to see which one caused the issue. Each Shipment will have a \"batch_status\" that will let you know which Shipment needs to be fixed. We also provide a summary of successes and failure so you know how many Shipments need attention.\n\nHere's an example of webhook for a Batch:\n\n```json\n{\n  \"id\": \"evt_...\",\n  \"object\": \"Event\",\n  \"description\": \"batch.updated\",\n  \"mode\": \"test\",\n  \"previous_attributes\": { \"state\": \"label_generating\" },\n  \"result\": {\n    \"id\": \"batch_...\",\n    \"object\": \"Batch\",\n    \"state\": \"label_generated\",\n    \"num_shipments\": 4,\n    \"label_url\": \"https://amazonaws.com/.../a1b2c3.pdf\",\n    \"shipments\": [\n      { \"id\": \"shp_...\", \"batch_status\": \"created\" },\n      \"...\"\n    ],\n    \"status\": { \"created\": 0, \"postage_purchased\": 4, \"postage_purchase_failed\": 0 }\n  }\n}\n```\n\n<div class=\"guides-related-cards\">\n  <a class=\"guides-related-card\" href=\"#\" data-guide-switch=\"webhooks\">\n    <div class=\"guides-related-card__text\">\n      <span class=\"guides-related-card__label\">RELATED LINK</span>\n      <span class=\"guides-related-card__title\">Webhooks Guide</span>\n    </div>\n    <svg class=\"guides-related-card__icon\" width=\"17\" height=\"17\" viewBox=\"0 0 17 17\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\"><path d=\"M8.25 11.25L11.25 8.25M11.25 8.25L8.25 5.25M11.25 8.25H5.25M15.75 8.25C15.75 12.3921 12.3921 15.75 8.25 15.75C4.10786 15.75 0.75 12.3921 0.75 8.25C0.75 4.10786 4.10786 0.75 8.25 0.75C12.3921 0.75 15.75 4.10786 15.75 8.25Z\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/></svg>\n  </a>\n</div>"
    },
    {
      "id": "customs",
      "title": "Customs Guide",
      "content": "# Customs Guide\n\nThis guide will teach you how to pass the necessary customs information for shipping internationally. In this example, we will be sending a customer in the UK a Vanlo T-Shirt and hat from our office in the US.\n\nWhen shipping internationally, you go through the same steps as shipping domestically, except that you need to add customs information to your shipment. Vanlo uses this information to automatically generate the necessary customs forms for your shipment. You need to pass customs information whenever you are shipping between two countries.\n\n<p class=\"guides-label\">Before You Start</p>\n\n<div class=\"guides-info\">\n  <p>Grab one of our <a href=\"https://github.com/VanloCorp\">official client libraries</a>.</p>\n  <p>If you haven't run through our <a href=\"#\" data-guide-switch=\"getting-started\">Getting Started Guide</a>, definitely do that before moving on to this one.</p>\n</div>\n\n<div class=\"guides-related-cards\">\n  <a class=\"guides-related-card\" href=\"#\" data-guide-switch=\"getting-started\">\n    <div class=\"guides-related-card__text\">\n      <span class=\"guides-related-card__label\">RELATED LINK</span>\n      <span class=\"guides-related-card__title\">Getting Started Guide</span>\n    </div>\n    <svg class=\"guides-related-card__icon\" width=\"17\" height=\"17\" viewBox=\"0 0 17 17\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\"><path d=\"M8.25 11.25L11.25 8.25M11.25 8.25L8.25 5.25M11.25 8.25H5.25M15.75 8.25C15.75 12.3921 12.3921 15.75 8.25 15.75C4.10786 15.75 0.75 12.3921 0.75 8.25C0.75 4.10786 4.10786 0.75 8.25 0.75C12.3921 0.75 15.75 4.10786 15.75 8.25Z\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/></svg>\n  </a>\n</div>\n\n\n## Step 1: Prepare Your Customs Items\n\nWhen shipping internationally, carriers require that you add information about the contents of your package. This information is used by the customs process for the country to which you are shipping.\n\nFor each type of item you are shipping, you'll describe it with a <a href=\"/#customsitem-object\">CustomsItem</a>. If you have multiples of the same item in the package, you don't need to create a new <a href=\"/#customsitem-object\">CustomsItem</a> for each - just specify the quantity. <a href=\"/#customsitem-object\">CustomsItems</a> are **not** a standalone resource: you don't POST them individually. Instead, you build the array of items locally and pass them inline inside the <a href=\"/#customsinfo-object\">CustomsInfo</a> you create in Step 2.\n\nEach <a href=\"/#customsitem-object\">CustomsItem</a> needs the following fields:\n\ndescription = A brief description of the item\n\nquantity = Number of that item contained in the package\n\nweight = Total weight in ounces of all the items of that type in the package\n\nvalue = Total value in US dollars of all the items of that type in the package\n\nhs_tariff_number = The six digit code for your item as specified by the <a href=\"http://hts.usitc.gov\">Harmonized System for tariffs</a>. We talk a bit more about this below.\n\norigin_country = Where the item was manufactured or assembled.\n\nTo get the `hs_tariff_number`, you'll need to look up the harmonization code associated with whatever product you are shipping. You can search for them on <a href=\"http://hts.usitc.gov\">hts.usitc.gov</a>.\n\nHere's how to build a <a href=\"/#customsitem-object\">CustomsItem</a> for the T-shirt we're shipping - note that none of these examples hit the API, they just construct the item data you'll hand to Step 2:\n\n\n### CustomsItems\n\n```shell\n# CustomsItems are not a standalone endpoint - build the JSON\n# structure locally and embed it in the customs_info POST below.\nITEM='{\n  \"description\": \"T-shirt\",\n  \"quantity\": 1,\n  \"weight\": 5,\n  \"value\": 10,\n  \"hs_tariff_number\": \"123456\",\n  \"origin_country\": \"US\"\n}'\n```\n\n```ruby\nrequire 'vanlo'\nVanlo.api_key = 'VANLO_API_KEY'\n\n# CustomsItems are built locally and passed to CustomsInfo in Step 2\nitem = {\n  description:      'T-shirt',\n  quantity:         1,\n  weight:           5,\n  value:            10,\n  hs_tariff_number: '123456',\n  origin_country:   'US'\n}\n```\n\n```python\nimport vanlo\nvanlo.api_key = 'VANLO_API_KEY'\n\n# CustomsItems are built locally and passed to CustomsInfo in Step 2\nitem = {\n  'description':      'T-shirt',\n  'quantity':         1,\n  'weight':           5,\n  'value':            10,\n  'hs_tariff_number': '123456',\n  'origin_country':   'US'\n}\n```\n\n```php\nrequire_once('/path/to/lib/vanlo.php');\n\\Vanlo\\Vanlo::setApiKey('VANLO_API_KEY');\n\n// CustomsItems are built locally and passed to CustomsInfo in Step 2\n$item = array(\n  'description'      => 'T-shirt',\n  'quantity'         => 1,\n  'weight'           => 5,\n  'value'            => 10,\n  'hs_tariff_number' => '123456',\n  'origin_country'   => 'US'\n);\n```\n\n```csharp\nusing Vanlo;\nClientManager.SetCurrent(\"VANLO_API_KEY\");\n\n// CustomsItems are built locally and passed to CustomsInfo in Step 2\nvar item = new Dictionary<string, object>() {\n    { \"description\",      \"T-shirt\" },\n    { \"quantity\",         1 },\n    { \"weight\",           5 },\n    { \"value\",            10 },\n    { \"hs_tariff_number\", \"123456\" },\n    { \"origin_country\",   \"US\" }\n};\n```\n\n<div class=\"guides-related-cards\">\n  <a class=\"guides-related-card\" href=\"#customsitem-object\">\n    <div class=\"guides-related-card__text\">\n      <span class=\"guides-related-card__label\">RELATED LINK</span>\n      <span class=\"guides-related-card__title\">CustomsItem Object</span>\n    </div>\n    <svg class=\"guides-related-card__icon\" width=\"17\" height=\"17\" viewBox=\"0 0 17 17\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\"><path d=\"M8.25 11.25L11.25 8.25M11.25 8.25L8.25 5.25M11.25 8.25H5.25M15.75 8.25C15.75 12.3921 12.3921 15.75 8.25 15.75C4.10786 15.75 0.75 12.3921 0.75 8.25C0.75 4.10786 4.10786 0.75 8.25 0.75C12.3921 0.75 15.75 4.10786 15.75 8.25Z\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/></svg>\n  </a>\n</div>\n\n\n## Step 2: Create a Customs Info Form\n\nOnce you've assembled the <a href=\"/#customsitem-object\">CustomsItem</a> objects for the items you are shipping, you pass them inline to the single POST that creates the customs form for your shipment. We call this form the <a href=\"/#customsinfo-object\">CustomsInfo</a> object. You will need only one <a href=\"/#customsinfo-object\">CustomsInfo</a> object per shipment.\n\nThe <a href=\"/#customsinfo-object\">CustomsInfo</a> object contains a bunch of attributes that are very specific to shipping goods across borders. Below we'll do our best to explain each though it can be a bit confusing. If you ever have any specific questions, don't hesitate to email us at support@vanlo.com.\n\nWhen creating a <a href=\"/#customsinfo-object\">CustomsInfo</a> object you need to pass:\n\ncustoms_items = An array of <a href=\"/#customsitem-object\">CustomsItem</a> objects. This array should contain any items you are shipping in your package.\n\ncontents_type = The type of item you are sending. You pass one of the following: 'merchandise', 'returned_goods', 'documents', 'gift', 'sample', 'other'.\n\ncontents_explanation = If you specify 'other' in the 'contents_type' attribute, you must supply a brief description in this attribute.\n\nrestriction_type = Describes if your shipment requires any special treatment / quarantine when entering the country. You pass one of the following: 'none', 'other', 'quarantine', 'sanitary_phytosanitary_inspection'.\n\nrestriction_comments = If the \"restriction_type\" attribute is not \"none\", you must supply a brief description of what is required.\n\ncustoms_certify = This is a boolean value (true, false) that takes the place of the signature on the physical customs form. This is how you indicate that the information you have provided is accurate.\n\ncustoms_signer = This is the name of the person who is certifying that the information provided on the customs form is accurate. Use a name of the person in your organization who is responsible for this.\n\nnon_delivery_option = In case the shipment cannot be delivered, this option tells the carrier what you want to happen to the package. You can pass either: 'abandon', 'return'. The value defaults to 'return'. If you pass 'abandon', you will not receive the package back if it cannot be delivered.\n\neel_pfc = When shipping outside the US, you need to provide either an Exemption and Exclusion Legend (EEL) code or a Proof of Filing Citation (PFC). Which you need is based on the value of the goods being shipped.\n\nIf the value of the goods is less than $2,500, then you pass the following EEL code: \"NOEEI 30.37(a)\"\n\nIf the value of the goods is greater than $2,500, you need to get an Automated Export System (AES) Internal Transaction Number (ITN) for your shipment. ITN will look like \"AES X20120502123456\". To get an ITN, go to the <a href=\"https://ace.cbp.gov/\">AESDirect</a> website.\n\nAn ITN is required for any international shipment valued over $2,500 and/or requires an export license unless exemptions apply.\n\nThe maximum number of items that can be included in customs info with UPS is 100.\n\nThe trickiest part of creating the <a href=\"/#customsinfo-object\">CustomsInfo</a> object is figuring out the values for the attributes. Once you've done that, it's simply a matter of passing those values to us. Here's an example of creating the <a href=\"/#customsinfo-object\">CustomsInfo</a> object for our shipment of T-shirt and Hat to the UK:\n\n\n### CustomsInfo (Customs Form)\n\n```shell\ncurl -X POST https://www.vanlo.com/api/v1/customs_infos \\\n  -H 'Authorization: Bearer VANLO_API_KEY' \\\n  -H 'Content-Type: application/json' \\\n  -d '{\n  \"customs_info\": {\n    \"customs_certify\": \"true\",\n    \"customs_signer\": \"Steve Brule\",\n    \"contents_type\": \"merchandise\",\n    \"contents_explanation\": \"\",\n    \"restriction_type\": \"none\",\n    \"eel_pfc\": \"NOEEI 30.37(a)\",\n    \"customs_items\": [\n      {\n        \"description\": \"T-shirt\",\n        \"quantity\": \"1\",\n        \"weight\": \"5\",\n        \"value\": \"10\",\n        \"hs_tariff_number\": \"123456\",\n        \"origin_country\": \"US\"\n      }\n    ]\n  }\n}'\n```\n\n```ruby\nrequire 'vanlo'\nVanlo.api_key = 'VANLO_API_KEY'\n\nVanlo::CustomsInfo.create(\n  customs_certify: true,\n  customs_signer: 'Steve Brule',\n  contents_type: 'merchandise',\n  contents_explanation: '',\n  restriction_type: 'none',\n  eel_pfc: 'NOEEI 30.37(a)',\n  customs_items: [\n    {\n      description: 'T-shirt',\n      quantity: 1,\n      weight: 5,\n      value: 10,\n      hs_tariff_number: '123456',\n      origin_country: 'US'\n    }\n  ]\n)\n```\n\n```python\nimport vanlo\nvanlo.api_key = 'VANLO_API_KEY'\n\nvanlo.CustomsInfo.create(\n  customs_certify=True,\n  customs_signer='Steve Brule',\n  contents_type='merchandise',\n  contents_explanation='',\n  restriction_type='none',\n  eel_pfc='NOEEI 30.37(a)',\n  customs_items=[{\n    'description': 'T-shirt',\n    'quantity': 1,\n    'weight': 5,\n    'value': 10,\n    'hs_tariff_number': '123456',\n    'origin_country': 'US'\n  }]\n)\n```\n\n```php\nrequire_once('/path/to/lib/vanlo.php');\n\\Vanlo\\Vanlo::setApiKey('VANLO_API_KEY');\n\n\\Vanlo\\CustomsInfo::create(array(\n  'customs_certify'      => true,\n  'customs_signer'       => 'Steve Brule',\n  'contents_type'        => 'merchandise',\n  'contents_explanation' => '',\n  'restriction_type'     => 'none',\n  'eel_pfc'              => 'NOEEI 30.37(a)',\n  'customs_items'        => array(array(\n    'description'      => 'T-shirt',\n    'quantity'         => 1,\n    'weight'           => 5,\n    'value'            => 10,\n    'hs_tariff_number' => '123456',\n    'origin_country'   => 'US'\n  ))\n));\n```\n\n```csharp\nusing Vanlo;\nClientManager.SetCurrent(\"VANLO_API_KEY\");\n\nDictionary<string, object> item = new Dictionary<string, object>() {\n    { \"description\",      \"T-shirt\" },\n    { \"quantity\",         1 },\n    { \"weight\",           5 },\n    { \"value\",            10 },\n    { \"hs_tariff_number\", \"123456\" },\n    { \"origin_country\",   \"US\" }\n};\n\nCustomsInfo info = CustomsInfo.Create(new Dictionary<string, object>() {\n    { \"customs_certify\",      true },\n    { \"customs_signer\",       \"Steve Brule\" },\n    { \"contents_type\",        \"merchandise\" },\n    { \"contents_explanation\", \"\" },\n    { \"restriction_type\",     \"none\" },\n    { \"eel_pfc\",              \"NOEEI 30.37(a)\" },\n    { \"customs_items\",        new List<Dictionary<string, object>>() { item } }\n});\n```\n\n\n### CustomsInfo JSON Response\n\n```json\n{\n  \"id\": \"cstinfo_...\",\n  \"object\": \"CustomsInfo\",\n  \"contents_explanation\": null,\n  \"contents_type\": \"gift\",\n  \"customs_certify\": true,\n  \"customs_signer\": \"Steve Brule\",\n  \"eel_pfc\": \"NOEEI 30.37(a)\",\n  \"non_delivery_option\": null,\n  \"restriction_comments\": null,\n  \"restriction_type\": null,\n  \"customs_items\": [\n    {\n      \"id\": \"cstitem_...\",\n      \"object\": \"CustomsItem\",\n      \"description\": \"T-shirt\",\n      \"quantity\": 1,\n      \"value\": 10,\n      \"weight\": 5,\n      \"hs_tariff_number\": \"123456\",\n      \"origin_country\": \"US\",\n      \"created_at\": \"2022-07-22T01:53:42Z\",\n      \"updated_at\": \"2022-07-22T01:53:42Z\"\n    }\n  ],\n  \"created_at\": \"2022-07-22T01:56:54Z\",\n  \"updated_at\": \"2022-07-22T01:56:54Z\"\n}\n```\n\n<div class=\"guides-related-cards\">\n  <a class=\"guides-related-card\" href=\"#customsinfo-object\">\n    <div class=\"guides-related-card__text\">\n      <span class=\"guides-related-card__label\">RELATED LINK</span>\n      <span class=\"guides-related-card__title\">CustomsInfo Object</span>\n    </div>\n    <svg class=\"guides-related-card__icon\" width=\"17\" height=\"17\" viewBox=\"0 0 17 17\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\"><path d=\"M8.25 11.25L11.25 8.25M11.25 8.25L8.25 5.25M11.25 8.25H5.25M15.75 8.25C15.75 12.3921 12.3921 15.75 8.25 15.75C4.10786 15.75 0.75 12.3921 0.75 8.25C0.75 4.10786 4.10786 0.75 8.25 0.75C12.3921 0.75 15.75 4.10786 15.75 8.25Z\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/></svg>\n  </a>\n</div>\n\n\n## Step 3: Create Shipment and Attach Customs Info\n\nNow that you've created the required customs information, you can now create your international shipment. The only difference from a domestic shipment is that you need to also pass the <a href=\"/#customsinfo-object\">CustomsInfo</a> object when creating a shipment.\n\nWhen you buy a shipping label for your shipment, we automatically create additional customs forms that you need. Most often, the customs form is integrated into the label and you can put it directly on your package. If you ever have any questions about a specific carrier, don't hesitate to email us.\n\nHere's an example where we are creating an international shipment:\n\n\n### Create International Shipment\n\n```shell\ncurl -X POST https://www.vanlo.com/api/v1/shipments \\\n  -H 'Authorization: Bearer VANLO_API_KEY' \\\n  -H 'Content-Type: application/json' \\\n  -d '{\n  \"shipment\": {\n    \"to_address\": {\n      \"name\": \"Tim Canterbury\",\n      \"company\": \"Wernham Hogg\",\n      \"street1\": \"118 Clippenham Lane\",\n      \"city\": \"Slough\",\n      \"zip\": \"SL15BE\",\n      \"country\": \"GB\"\n    },\n    \"from_address\": {\n      \"company\": \"Vanlo\",\n      \"street1\": \"5th Floor\",\n      \"city\": \"San Francisco\",\n      \"state\": \"CA\",\n      \"zip\": \"94104\",\n      \"country\": \"US\",\n      \"phone\": \"415-528-7555\"\n    },\n    \"parcel\": {\n      \"length\": \"9\",\n      \"width\": \"6\",\n      \"height\": \"3\",\n      \"weight\": \"20\"\n    },\n    \"customs_info\": {\n      \"id\": \"cstinfo_...\"\n    }\n  }\n}'\n```\n\n```ruby\nrequire 'vanlo'\nVanlo.api_key = 'VANLO_API_KEY'\n\nVanlo::Shipment.create(\n  to_address: {\n    name: 'Tim Canterbury',\n    company: 'Wernham Hogg',\n    street1: '118 Clippenham Lane',\n    city: 'Slough',\n    zip: 'SL15BE',\n    country: 'GB'\n  },\n  from_address: {\n    company: 'Vanlo',\n    street1: '5th Floor',\n    city: 'San Francisco',\n    state: 'CA',\n    zip: '94104',\n    country: 'US',\n    phone: '415-528-7555'\n  },\n  parcel: {\n    length: 9,\n    width: 6,\n    height: 3,\n    weight: 20\n  },\n  customs_info: { id: 'cstinfo_...' }\n)\n```\n\n```python\nimport vanlo\nvanlo.api_key = 'VANLO_API_KEY'\n\nvanlo.Shipment.create(\n  to_address={\n    'name': 'Tim Canterbury',\n    'company': 'Wernham Hogg',\n    'street1': '118 Clippenham Lane',\n    'city': 'Slough',\n    'zip': 'SL15BE',\n    'country': 'GB'\n  },\n  from_address={\n    'company': 'Vanlo',\n    'street1': '5th Floor',\n    'city': 'San Francisco',\n    'state': 'CA',\n    'zip': '94104',\n    'country': 'US',\n    'phone': '415-528-7555'\n  },\n  parcel={\n    'length': 9,\n    'width': 6,\n    'height': 3,\n    'weight': 20\n  },\n  customs_info={ 'id': 'cstinfo_...' }\n)\n```\n\n```php\nrequire_once('/path/to/lib/vanlo.php');\n\\Vanlo\\Vanlo::setApiKey('VANLO_API_KEY');\n\n\\Vanlo\\Shipment::create(array(\n  'to_address' => array(\n    'name'    => 'Tim Canterbury',\n    'company' => 'Wernham Hogg',\n    'street1' => '118 Clippenham Lane',\n    'city'    => 'Slough',\n    'zip'     => 'SL15BE',\n    'country' => 'GB'\n  ),\n  'from_address' => array(\n    'company' => 'Vanlo',\n    'street1' => '5th Floor',\n    'city'    => 'San Francisco',\n    'state'   => 'CA',\n    'zip'     => '94104',\n    'country' => 'US',\n    'phone'   => '415-528-7555'\n  ),\n  'parcel' => array(\n    'length' => 9,\n    'width'  => 6,\n    'height' => 3,\n    'weight' => 20\n  ),\n  'customs_info' => array('id' => 'cstinfo_...')\n));\n```\n\n```csharp\nusing Vanlo;\nClientManager.SetCurrent(\"VANLO_API_KEY\");\n\nShipment shipment = Shipment.Create(new Dictionary<string, object>() {\n    { \"to_address\", new Dictionary<string, object>() {\n        { \"name\",    \"Tim Canterbury\" },\n        { \"company\", \"Wernham Hogg\" },\n        { \"street1\", \"118 Clippenham Lane\" },\n        { \"city\",    \"Slough\" },\n        { \"zip\",     \"SL15BE\" },\n        { \"country\", \"GB\" }\n    } },\n    { \"from_address\", new Dictionary<string, object>() {\n        { \"company\", \"Vanlo\" },\n        { \"street1\", \"5th Floor\" },\n        { \"city\",    \"San Francisco\" },\n        { \"state\",   \"CA\" },\n        { \"zip\",     \"94104\" },\n        { \"country\", \"US\" },\n        { \"phone\",   \"415-528-7555\" }\n    } },\n    { \"parcel\", new Dictionary<string, object>() {\n        { \"length\", 9 },\n        { \"width\",  6 },\n        { \"height\", 3 },\n        { \"weight\", 20 }\n    } },\n    { \"customs_info\", new Dictionary<string, object>() {\n        { \"id\", \"cstinfo_...\" }\n    } }\n});\n```\n\n\n### Create International Shipment Response\n\n> The above request returns the newly created international Shipment with the attached CustomsInfo. The `rates` array will populate shortly. The `postage_label`, `selected_rate`, and `tracking_code` fields remain `null` until you purchase a label in the next step.\n\n```json\n{\n  \"id\": \"shp_...\",\n  \"object\": \"Shipment\",\n  \"mode\": \"test\",\n  \"to_address\":   { \"id\": \"adr_...\", \"name\": \"Tim Canterbury\", \"country\": \"GB\", \"city\": \"Slough\", \"zip\": \"SL15BE\" },\n  \"from_address\": { \"id\": \"adr_...\", \"company\": \"Vanlo\", \"country\": \"US\", \"city\": \"San Francisco\", \"state\": \"CA\" },\n  \"parcel\":       { \"id\": \"prcl_...\", \"length\": 9, \"width\": 6, \"height\": 3, \"weight\": 20 },\n  \"customs_info\": {\n    \"id\": \"cstinfo_...\",\n    \"object\": \"CustomsInfo\",\n    \"contents_type\": \"gift\",\n    \"customs_certify\": true,\n    \"customs_signer\": \"Tim Canterbury\",\n    \"eel_pfc\": \"NOEEI 30.37(a)\",\n    \"customs_items\": [\n      { \"id\": \"cstitem_...\", \"description\": \"T-shirt\", \"quantity\": 1, \"value\": 11, \"weight\": 6, \"hs_tariff_number\": \"610910\", \"origin_country\": \"US\" },\n      { \"id\": \"cstitem_...\", \"description\": \"Hat\",     \"quantity\": 1, \"value\": 20, \"weight\": 14, \"hs_tariff_number\": \"650400\", \"origin_country\": \"US\" }\n    ]\n  },\n  \"rates\": [],\n  \"selected_rate\": null,\n  \"postage_label\": null,\n  \"tracking_code\": null\n}\n```\n\n\n## Step 4: Buy the International Label\n\nWith your international Shipment created and `CustomsInfo` attached, the final step is to purchase a label. Just like a domestic shipment, you call the buy endpoint with the rate you want to use - either one you picked from the `rates` array or the lowest rate via the client library helpers.\n\nWhen you buy an international label, Vanlo automatically generates any customs paperwork that needs to accompany it. For most carriers, the customs form is integrated into the label image itself, so you just print it, attach it, and ship. The label URL is returned on `postage_label.label_url`, and the tracking code is returned on `tracking_code`.\n\n\n### Buy International Shipment\n\n```shell\ncurl -X POST https://www.vanlo.com/api/v1/shipments/shp_.../buy \\\n  -H 'Authorization: Bearer VANLO_API_KEY' \\\n  -H 'Content-Type: application/json' \\\n  -d '{\n  \"rate\": {\n    \"id\": \"rate_...\"\n  }\n}'\n```\n\n```ruby\nrequire 'vanlo'\nVanlo.api_key = 'VANLO_API_KEY'\n\nshipment = Vanlo::Shipment.retrieve(\"shp_...\")\nshipment.buy(rate: shipment.lowest_rate)\n```\n\n```python\nimport vanlo\nvanlo.api_key = 'VANLO_API_KEY'\n\nshipment = vanlo.Shipment.retrieve(\"shp_...\")\nshipment.buy(rate=shipment.lowest_rate())\n```\n\n```php\nrequire_once(\"/path/to/lib/vanlo.php\");\n\\Vanlo\\Vanlo::setApiKey('VANLO_API_KEY');\n\n$shipment = \\Vanlo\\Shipment::retrieve(\"shp_...\");\n$shipment->buy(array(\n  'rate' => $shipment->lowest_rate()\n));\n```\n\n```csharp\nusing Vanlo;\nClientManager.SetCurrent(\"VANLO_API_KEY\");\n\nShipment shipment = Shipment.Retrieve(\"shp_...\");\nRate lowestRate = shipment.LowestRate();\n\nshipment.Buy(lowestRate);\n```\n\n\n### International Label Results\n\n```json\n{\n  \"id\": \"shp_...\",\n  \"object\": \"Shipment\",\n  \"mode\": \"test\",\n  \"customs_info\": { \"id\": \"cstinfo_...\", \"object\": \"CustomsInfo\" },\n  \"postage_label\": {\n    \"id\": \"pl_...\",\n    \"object\": \"PostageLabel\",\n    \"label_file_type\": \"image/png\",\n    \"label_url\": \"https://amazonaws.com/.../a1b2c3.png\",\n    \"label_pdf_url\": null,\n    \"label_zpl_url\": null\n  },\n  \"tracking_code\": \"LN123456789US\",\n  \"selected_rate\": {\n    \"id\": \"rate_...\",\n    \"service\": \"PriorityMailInternational\",\n    \"rate\": \"48.75\",\n    \"carrier\": \"USPS\"\n  },\n  \"tracker\": { \"id\": \"trk_...\", \"object\": \"Tracker\" }\n}\n```\n\nYou've just shipped your first international package with Vanlo! To set up delivery notifications for your customer, head over to our <a href=\"#\" data-guide-switch=\"tracking\">Tracking Guide</a>.\n\n\n<div class=\"guides-related-cards\">\n  <a class=\"guides-related-card\" href=\"#shipment-object\">\n    <div class=\"guides-related-card__text\">\n      <span class=\"guides-related-card__label\">RELATED LINK</span>\n      <span class=\"guides-related-card__title\">Shipment Object</span>\n    </div>\n    <svg class=\"guides-related-card__icon\" width=\"17\" height=\"17\" viewBox=\"0 0 17 17\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\"><path d=\"M8.25 11.25L11.25 8.25M11.25 8.25L8.25 5.25M11.25 8.25H5.25M15.75 8.25C15.75 12.3921 12.3921 15.75 8.25 15.75C4.10786 15.75 0.75 12.3921 0.75 8.25C0.75 4.10786 4.10786 0.75 8.25 0.75C12.3921 0.75 15.75 4.10786 15.75 8.25Z\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/></svg>\n  </a>\n</div>"
    },
    {
      "id": "getting_started",
      "title": "Getting Started",
      "content": "# Getting Started\n\nThis guide will walk you through shipping your first package with Vanlo. In this example, we'll be shipping a Vanlo T-Shirt from Vanlo HQ to a customer.\n\n<p class=\"guides-label\">Before You Start</p>\n\n<div class=\"guides-info\">\n  <p>Get invited to the Vanlo platform. If you are looking for an invite, please contact Vanlo by emailing <a href=\"mailto:support@vanlo.com\">support@vanlo.com</a> and request an invite. Once you are logged in we recommend starting with test API credentials. Use find your test API key by toggling into Test Mode in the bottom left corner of your dashboard and go to the <a href=\"https://dashboard.vanlo.com/apikey\">API Keys tab under the Developers Menu</a>.</p>\n  <p><a href=\"https://github.com/VanloCorp\">Download an Vanlo Client Library</a> in one following languages: Python, Ruby, PHP, Java, and C# (.NET). We also have community-supported client libraries like Perl and iOS on our Integrations page. If you prefer, you can always directly interact with the REST API with cURL.</p>\n  <p>Read the <a href=\"/#introduction\">Vanlo Full API Docs</a> to get more details about each object discussed in this guide. You'll need to know these few details to understand some of the code samples, and how to optimize your application.</p>\n</div>\n\n\n## Step 1: Create To and From Addresses\n\nTo start, create the To and From Addresses for the package you'll be shipping. An Address object contains information you'd expect like name, street, city, state, country, etc. You need to create an Address object for both the To and the From Addresses.\n\nOnce you create an Address, Vanlo returns a unique ID for the Address. You can reuse this ID in the future for other packages you ship. This is helpful for when you are sending a lot of packages from a single location. For every type of object you create on Vanlo, we will pass you back a unique ID that you can use to reference in the future.\n\n\n### From Address\n\n```shell\ncurl -X POST https://www.vanlo.com/api/v1/addresses \\\n  -H 'Authorization: Bearer VANLO_API_KEY' \\\n  -H 'Content-Type: application/json' \\\n  -d '{\n  \"address\": {\n    \"company\": \"Vanlo\",\n    \"street1\": \"417 Montgomery Street\",\n    \"street2\": \"5th Floor\",\n    \"city\": \"San Francisco\",\n    \"state\": \"CA\",\n    \"zip\": \"94104\",\n    \"phone\": \"415-528-7555\"\n  }\n}'\n```\n\n```ruby\nrequire 'vanlo'\nVanlo.api_key = 'VANLO_API_KEY'\n\nVanlo::Address.create(\n  company: \"Vanlo\",\n  street1: \"417 Montgomery Street\",\n  street2: \"5th Floor\",\n  city: \"San Francisco\",\n  state: \"CA\",\n  zip: \"94104\",\n  phone: \"415-528-7555\"\n)\n```\n\n```python\nimport vanlo\nvanlo.api_key = 'VANLO_API_KEY'\n\nvanlo.Address.create(\n  company=\"Vanlo\",\n  street1=\"417 Montgomery Street\",\n  street2=\"5th Floor\",\n  city=\"San Francisco\",\n  state=\"CA\",\n  zip=\"94104\",\n  phone=\"415-528-7555\"\n)\n```\n\n```php\nrequire_once(\"/path/to/lib/vanlo.php\");\n\\Vanlo\\Vanlo::setApiKey('VANLO_API_KEY');\n\n$address_params = array(\n  \"company\" => \"Vanlo\",\n  \"street1\" => \"417 Montgomery Street\",\n  \"street2\" => \"5th Floor\",\n  \"city\" => \"San Francisco\",\n  \"state\" => \"CA\",\n  \"zip\" => \"94104\",\n  \"phone\" => \"415-528-7555\"\n);\n\n\\Vanlo\\Address::create($address_params);\n```\n\n```csharp\nusing Vanlo;\nClientManager.SetCurrent(\"VANLO_API_KEY\");\n\nAddress address = Address.Create(\n    new Dictionary<string, object>() {\n        { \"company\", \"Vanlo\" },\n        { \"street1\", \"417 Montgomery Street\" },\n        { \"street2\", \"5th Floor\" },\n        { \"city\", \"San Francisco\" },\n        { \"state\", \"CA\" },\n        { \"zip\", \"94104\" },\n        { \"phone\", \"415-528-7555\" }\n    }\n);\n```\n\n\n### From Address Response\n\n```json\n{\n  \"id\": \"adr_...\",\n  \"object\": \"Address\",\n  \"created_at\": \"2014-07-10T01:05:57Z\",\n  \"updated_at\": \"2014-07-10T01:05:57Z\",\n  \"name\": null,\n  \"company\": \"Vanlo\",\n  \"street1\": \"417 Montgomery Street\",\n  \"street2\": \"5th Floor\",\n  \"city\": \"San Francisco\",\n  \"state\": \"CA\",\n  \"zip\": \"94104\",\n  \"country\": \"US\",\n  \"phone\": \"4155287555\",\n  \"email\": null,\n  \"mode\": \"test\",\n  \"carrier_facility\": null,\n  \"residential\": null,\n  \"federal_tax_id\": null,\n  \"state_tax_id\": null,\n  \"verifications\": {}\n}\n```\n\n\n### To Address\n\n```shell\ncurl -X POST https://www.vanlo.com/api/v1/addresses \\\n  -H 'Authorization: Bearer VANLO_API_KEY' \\\n  -H 'Content-Type: application/json' \\\n  -d '{\n  \"address\": {\n    \"name\": \"George Costanza\",\n    \"company\": \"Vandelay Industries\",\n    \"street1\": \"1 E 161st St.\",\n    \"city\": \"Bronx\",\n    \"state\": \"NY\",\n    \"zip\": \"10451\"\n  }\n}'\n```\n\n```ruby\nrequire 'vanlo'\nVanlo.api_key = 'VANLO_API_KEY'\n\nVanlo::Address.create(\n  name: \"George Costanza\",\n  company: \"Vandelay Industries\",\n  street1: \"1 E 161st St.\",\n  city: \"Bronx\",\n  state: \"NY\",\n  zip: \"10451\"\n)\n```\n\n```python\nimport vanlo\nvanlo.api_key = 'VANLO_API_KEY'\n\nvanlo.Address.create(\n  name=\"George Costanza\",\n  company=\"Vandelay Industries\",\n  street1=\"1 E 161st St.\",\n  city=\"Bronx\",\n  state=\"NY\",\n  zip=\"10451\"\n)\n```\n\n```php\nrequire_once(\"/path/to/lib/vanlo.php\");\n\\Vanlo\\Vanlo::setApiKey('VANLO_API_KEY');\n\n$address_params = array(\n  \"name\" => \"George Costanza\",\n  \"company\" => \"Vandelay Industries\",\n  \"street1\" => \"1 E 161st St.\",\n  \"city\" => \"Bronx\",\n  \"state\" => \"NY\",\n  \"zip\" => \"10451\"\n);\n\n\\Vanlo\\Address::create($address_params);\n```\n\n```csharp\nusing Vanlo;\nClientManager.SetCurrent(\"VANLO_API_KEY\");\n\nAddress address = Address.Create(\n    new Dictionary<string, object>() {\n        { \"name\", \"George Costanza\" },\n        { \"company\", \"Vandelay Industries\" },\n        { \"street1\", \"1 E 161st St.\" },\n        { \"city\", \"Bronx\" },\n        { \"state\", \"NY\" },\n        { \"zip\", \"10451\" }\n    }\n);\n```\n\n\n### To Address Response\n\n```json\n{\n  \"id\": \"adr_...\",\n  \"object\": \"Address\",\n  \"created_at\": \"2014-07-10T01:06:04Z\",\n  \"updated_at\": \"2014-07-10T01:06:04Z\",\n  \"name\": \"George Costanza\",\n  \"company\": \"Vandelay Industries\",\n  \"street1\": \"1 E 161st St.\",\n  \"street2\": null,\n  \"city\": \"Bronx\",\n  \"state\": \"NY\",\n  \"zip\": \"10451\",\n  \"country\": \"US\",\n  \"phone\": null,\n  \"email\": null,\n  \"mode\": \"test\",\n  \"carrier_facility\": null,\n  \"residential\": null,\n  \"federal_tax_id\": null,\n  \"state_tax_id\": null,\n  \"verifications\": {}\n}\n```\n\n<div class=\"guides-related-cards\">\n  <a class=\"guides-related-card\" href=\"#address-object\">\n    <div class=\"guides-related-card__text\">\n      <span class=\"guides-related-card__label\">RELATED LINK</span>\n      <span class=\"guides-related-card__title\">Address Object</span>\n    </div>\n    <svg class=\"guides-related-card__icon\" width=\"17\" height=\"17\" viewBox=\"0 0 17 17\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\"><path d=\"M8.25 11.25L11.25 8.25M11.25 8.25L8.25 5.25M11.25 8.25H5.25M15.75 8.25C15.75 12.3921 12.3921 15.75 8.25 15.75C4.10786 15.75 0.75 12.3921 0.75 8.25C0.75 4.10786 4.10786 0.75 8.25 0.75C12.3921 0.75 15.75 4.10786 15.75 8.25Z\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/></svg>\n  </a>\n  <a class=\"guides-related-card\" href=\"#\" data-guide-switch=\"address-verification\">\n    <div class=\"guides-related-card__text\">\n      <span class=\"guides-related-card__label\">RELATED LINK</span>\n      <span class=\"guides-related-card__title\">Address Verification Guide</span>\n    </div>\n    <svg class=\"guides-related-card__icon\" width=\"17\" height=\"17\" viewBox=\"0 0 17 17\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\"><path d=\"M8.25 11.25L11.25 8.25M11.25 8.25L8.25 5.25M11.25 8.25H5.25M15.75 8.25C15.75 12.3921 12.3921 15.75 8.25 15.75C4.10786 15.75 0.75 12.3921 0.75 8.25C0.75 4.10786 4.10786 0.75 8.25 0.75C12.3921 0.75 15.75 4.10786 15.75 8.25Z\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/></svg>\n  </a>\n</div>\n\n\n## Step 2: Create a Parcel\n\nNext, you'll need to tell us about the package you're shipping. You do this by creating a Parcel object. A Parcel contains information about the weight and dimensions (length, width, and height) of a package. Weights are in ounces and dimensions are in inches. In this example, because it's a t-shirt, it's a pretty small and light package. When you create a Parcel, Vanlo will give you a unique ID that you can use to reference it after the fact.\n\nWe also have common carrier-specific packaging as predefined constants you can use. If you're using this for your packaging, you don't need to pass the dimensions, just the weight. You do this by passing the constant in the \"predefined_package\" parameter and we take care of the rest. Here's the full list of predefined packages we support.\n\n\n### Parcel\n\n```shell\ncurl -X POST https://www.vanlo.com/api/v1/parcels \\\n  -H 'Authorization: Bearer VANLO_API_KEY' \\\n  -H 'Content-Type: application/json' \\\n  -d '{\n  \"parcel\": {\n    \"length\": \"9\",\n    \"width\": \"6\",\n    \"height\": \"2\",\n    \"weight\": \"10\"\n  }\n}'\n```\n\n```ruby\nrequire 'vanlo'\nVanlo.api_key = 'VANLO_API_KEY'\n\nVanlo::Parcel.create(\n  length: 9,\n  width: 6,\n  height: 2,\n  weight: 10\n)\n```\n\n```python\nimport vanlo\nvanlo.api_key = 'VANLO_API_KEY'\n\nvanlo.Parcel.create(\n  length=9,\n  width=6,\n  height=2,\n  weight=10\n)\n```\n\n```php\nrequire_once(\"/path/to/lib/vanlo.php\");\n\\Vanlo\\Vanlo::setApiKey('VANLO_API_KEY');\n\n\\Vanlo\\Parcel::create(array(\n    \"length\" => 9,\n    \"width\" => 6,\n    \"height\" => 2,\n    \"weight\" => 10\n));\n```\n\n```csharp\nusing Vanlo;\nClientManager.SetCurrent(\"VANLO_API_KEY\");\n\nParcel parcel = Parcel.Create(new Dictionary<string, object>() {\n    { \"length\", 9 },\n    { \"width\", 6 },\n    { \"height\", 2 },\n    { \"weight\", 10 }\n});\n```\n\n\n### Parcel Response\n\n```json\n{\n  \"id\": \"prcl_...\",\n  \"object\": \"Parcel\",\n  \"length\": 9.0,\n  \"width\": 6.0,\n  \"height\": 2.0,\n  \"predefined_package\": null,\n  \"weight\": 10.0,\n  \"created_at\": \"2014-07-10T01:06:14Z\",\n  \"updated_at\": \"2014-07-10T01:06:14Z\"\n}\n```\n\n\n### Predefined Parcel\n\n```shell\ncurl -X POST https://www.vanlo.com/api/v1/parcels \\\n  -H 'Authorization: Bearer VANLO_API_KEY' \\\n  -H 'Content-Type: application/json' \\\n  -d '{\n  \"parcel\": {\n    \"predefined_package\": \"FlatRateEnvelope\",\n    \"weight\": \"10\"\n  }\n}'\n```\n\n```ruby\nrequire 'vanlo'\nVanlo.api_key = 'VANLO_API_KEY'\n\nVanlo::Parcel.create(\n  predefined_package: \"FlatRateEnvelope\",\n  weight: 10\n)\n```\n\n```python\nimport vanlo\nvanlo.api_key = 'VANLO_API_KEY'\n\nvanlo.Parcel.create(\n  predefined_package=\"FlatRateEnvelope\",\n  weight=10\n)\n```\n\n```php\nrequire_once(\"/path/to/lib/vanlo.php\");\n\\Vanlo\\Vanlo::setApiKey('VANLO_API_KEY');\n\n\\Vanlo\\Parcel::create(array(\n    \"predefined_package\" => \"FlatRateEnvelope\",\n    \"weight\" => 10\n));\n```\n\n```csharp\nusing Vanlo;\nClientManager.SetCurrent(\"VANLO_API_KEY\");\n\nParcel parcel = Parcel.Create(new Dictionary<string, object>() {\n    { \"predefined_package\", \"FlatRateEnvelope\" },\n    { \"weight\", 10 }\n});\n```\n\n\n### Predefined Parcel Response\n\n```json\n{\n  \"id\": \"prcl_...\",\n  \"object\": \"Parcel\",\n  \"length\": null,\n  \"width\": null,\n  \"height\": null,\n  \"predefined_package\": \"FlatRateEnvelope\",\n  \"weight\": 10.0,\n  \"created_at\": \"2014-07-10T01:06:24Z\",\n  \"updated_at\": \"2014-07-10T01:06:24Z\"\n}\n```\n\n<div class=\"guides-related-cards\">\n  <a class=\"guides-related-card\" href=\"#parcel-object\">\n    <div class=\"guides-related-card__text\">\n      <span class=\"guides-related-card__label\">RELATED LINK</span>\n      <span class=\"guides-related-card__title\">Parcel Object</span>\n    </div>\n    <svg class=\"guides-related-card__icon\" width=\"17\" height=\"17\" viewBox=\"0 0 17 17\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\"><path d=\"M8.25 11.25L11.25 8.25M11.25 8.25L8.25 5.25M11.25 8.25H5.25M15.75 8.25C15.75 12.3921 12.3921 15.75 8.25 15.75C4.10786 15.75 0.75 12.3921 0.75 8.25C0.75 4.10786 4.10786 0.75 8.25 0.75C12.3921 0.75 15.75 4.10786 15.75 8.25Z\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/></svg>\n  </a>\n</div>\n\n\n## Step 3: Create a Shipment and Get Rates\n\nNow that you have created To and From Addresses and a Parcel, you can combine them all by creating a Shipment. When you create a Shipment, the API responds with shipping rates for the all carriers you've enabled. These are the rates for shipping the Parcel between the To and From Addresses you've specified.\n\nTo create a Shipment, pass us the IDs of the To and From Addresses and Parcel that you created in the previous steps. If creation is successful, we'll return a set of rates back to you. An individual rate contains information about the carrier, the service level (eg 1 day, 2 day, Ground, etc), cost, and the estimated number of days to delivery (when available). In our example, we'll keep it simple and just use USPS as the carrier.\n\nNote: Unless you've entered your carrier information for other carriers, you'll just receive USPS rates.\n\n\n### Create Shipment\n\n```shell\ncurl -X POST https://www.vanlo.com/api/v1/shipments \\\n  -H 'Authorization: Bearer VANLO_API_KEY' \\\n  -H 'Content-Type: application/json' \\\n  -d '{\n  \"shipment\": {\n    \"to_address\": {\n      \"id\": \"adr_...\"\n    },\n    \"from_address\": {\n      \"id\": \"adr_...\"\n    },\n    \"parcel\": {\n      \"id\": \"prcl_...\"\n    }\n  }\n}'\n```\n\n```ruby\nrequire 'vanlo'\nVanlo.api_key = 'VANLO_API_KEY'\n\nVanlo::Shipment.create(\n  to_address: { id: \"adr_...\" },\n  from_address: { id: \"adr_...\" },\n  parcel: { id: \"prcl_...\" }\n)\n```\n\n```python\nimport vanlo\nvanlo.api_key = 'VANLO_API_KEY'\n\nvanlo.Shipment.create(\n  to_address={ \"id\": \"adr_...\" },\n  from_address={ \"id\": \"adr_...\" },\n  parcel={ \"id\": \"prcl_...\" }\n)\n```\n\n```php\nrequire_once(\"/path/to/lib/vanlo.php\");\n\\Vanlo\\Vanlo::setApiKey('VANLO_API_KEY');\n\n$shipment = \\Vanlo\\Shipment::create(array(\n  \"to_address\"   => array(\"id\" => \"adr_...\"),\n  \"from_address\" => array(\"id\" => \"adr_...\"),\n  \"parcel\"       => array(\"id\" => \"prcl_...\")\n));\n```\n\n```csharp\nusing Vanlo;\nClientManager.SetCurrent(\"VANLO_API_KEY\");\n\nShipment shipment = Shipment.Create(new Dictionary<string, object>() {\n    { \"to_address\",   new Dictionary<string, object>() { { \"id\", \"adr_...\" } } },\n    { \"from_address\", new Dictionary<string, object>() { { \"id\", \"adr_...\" } } },\n    { \"parcel\",       new Dictionary<string, object>() { { \"id\", \"prcl_...\" } } }\n});\n```\n\n\n### Printing Results\n\n```json\n{\n  \"rates\": [\n    {\n      \"id\": \"rate_...\",\n      \"object\": \"Rate\",\n      \"created_at\": \"2014-07-10T01:06:46Z\",\n      \"updated_at\": \"2014-07-10T01:06:46Z\",\n      \"service\": \"First\",\n      \"rate\": \"2.93\",\n      \"currency\": \"USD\",\n      \"est_delivery_days\": 0,\n      \"carrier\": \"USPS\",\n      \"shipment_id\": \"shp_...\"\n    },\n    {\n      \"id\": \"rate_...\",\n      \"object\": \"Rate\",\n      \"created_at\": \"2014-07-10T01:06:46Z\",\n      \"updated_at\": \"2014-07-10T01:06:46Z\",\n      \"service\": \"Priority\",\n      \"rate\": \"6.51\",\n      \"currency\": \"USD\",\n      \"est_delivery_days\": null,\n      \"carrier\": \"USPS\",\n      \"shipment_id\": \"shp_...\"\n    }\n  ]\n}\n```\n\n<div class=\"guides-related-cards\">\n  <a class=\"guides-related-card\" href=\"#shipment-object\">\n    <div class=\"guides-related-card__text\">\n      <span class=\"guides-related-card__label\">RELATED LINK</span>\n      <span class=\"guides-related-card__title\">Shipment Object</span>\n    </div>\n    <svg class=\"guides-related-card__icon\" width=\"17\" height=\"17\" viewBox=\"0 0 17 17\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\"><path d=\"M8.25 11.25L11.25 8.25M11.25 8.25L8.25 5.25M11.25 8.25H5.25M15.75 8.25C15.75 12.3921 12.3921 15.75 8.25 15.75C4.10786 15.75 0.75 12.3921 0.75 8.25C0.75 4.10786 4.10786 0.75 8.25 0.75C12.3921 0.75 15.75 4.10786 15.75 8.25Z\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/></svg>\n  </a>\n  <a class=\"guides-related-card\" href=\"#rate-object\">\n    <div class=\"guides-related-card__text\">\n      <span class=\"guides-related-card__label\">RELATED LINK</span>\n      <span class=\"guides-related-card__title\">Rates Object</span>\n    </div>\n    <svg class=\"guides-related-card__icon\" width=\"17\" height=\"17\" viewBox=\"0 0 17 17\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\"><path d=\"M8.25 11.25L11.25 8.25M11.25 8.25L8.25 5.25M11.25 8.25H5.25M15.75 8.25C15.75 12.3921 12.3921 15.75 8.25 15.75C4.10786 15.75 0.75 12.3921 0.75 8.25C0.75 4.10786 4.10786 0.75 8.25 0.75C12.3921 0.75 15.75 4.10786 15.75 8.25Z\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/></svg>\n  </a>\n</div>\n\n\n## Step 4: Buy and Generate a Shipping Label\n\nYou're almost done. Now you're ready to buy the label so you can print it out and put it on your package. To do this, you need to tell us which rate you want to use. If you are using a client library, just call the buy method on the shipment and pass the ID of the rate you want to use. If using the REST API, then POST the rate ID to the shipment resource. We have examples of both below. We also have convenience functions built into the client libraries so you buy the lowest rate every time if that's easier for you.\n\nOnce you buy the label, we'll respond with a url to the image of your label that you can download and print. The url lives on the returned Shipment at `postage_label.label_url`.\n\nBy default, labels are returned in PNG format. To receive the label in a different format, pass a `label_format` value inside the `options` object when creating the shipment (for example, `\"options\": { \"label_format\": \"PDF\" }`). Supported formats are `PNG`, `PDF`, and `ZPL`. When you request `PDF` or `ZPL`, the URL for that format appears on the matching response field (`label_pdf_url` or `label_zpl_url`) alongside the base `label_url`. For multiparcel shipments the default format is `PDF` unless you explicitly request `ZPL`.\n\nIn the response, you'll also notice that we pass back the tracking ID for your package. You can use this to store on your side or pass to your customers. Vanlo supports sending automatic tracking updates to your app with webhooks. We'll teach you a bit more about tracking in the <a href=\"#\" data-guide-switch=\"tracking\">Tracking Guide</a>.\n\n\n### Buy Shipment\n\n```shell\ncurl -X POST https://www.vanlo.com/api/v1/shipments/shp_.../buy \\\n  -H 'Authorization: Bearer VANLO_API_KEY' \\\n  -H 'Content-Type: application/json' \\\n  -d '{\n  \"rate\": {\n    \"id\": \"rate_...\"\n  }\n}'\n```\n\n```ruby\nrequire 'vanlo'\nVanlo.api_key = 'VANLO_API_KEY'\n\nshipment = Vanlo::Shipment.retrieve(\"shp_...\")\nshipment.buy(rate: shipment.lowest_rate)\n```\n\n```python\nimport vanlo\nvanlo.api_key = 'VANLO_API_KEY'\n\nshipment = vanlo.Shipment.retrieve(\"shp_...\")\nshipment.buy(rate=shipment.lowest_rate())\n```\n\n```php\nrequire_once(\"/path/to/lib/vanlo.php\");\n\\Vanlo\\Vanlo::setApiKey('VANLO_API_KEY');\n\n$shipment = \\Vanlo\\Shipment::retrieve(\"shp_...\");\n$shipment->buy(array(\n  'rate' => $shipment->lowest_rate()\n));\n```\n\n```csharp\nusing Vanlo;\nClientManager.SetCurrent(\"VANLO_API_KEY\");\n\nShipment shipment = Shipment.Retrieve(\"shp_...\");\nRate lowestRate = shipment.LowestRate();\n\nshipment.Buy(lowestRate);\n```\n\n\n### Printing Results\n\n```json\n{\n  \"postage_label\": {\n    \"id\": \"pl_...\",\n    \"object\": \"PostageLabel\",\n    \"label_file_type\": \"image/png\",\n    \"label_url\": \"https://amazonaws.com/.../a1b2c3.png\",\n    \"label_pdf_url\": null,\n    \"label_zpl_url\": null\n  },\n  \"tracking_code\": \"9499907123456123456781\",\n  \"selected_rate\": {\n    \"id\": \"rate_...\",\n    \"service\": \"First\",\n    \"rate\": \"2.25\",\n    \"carrier\": \"USPS\"\n  },\n  \"tracker\": { \"id\": \"trk_...\", \"object\": \"Tracker\" }\n}\n```\n\nCongratulations! You've just shipped your first package with Vanlo! Check out our <a href=\"/#introduction\">Full Reference API Documentation</a>.\n\n\n## Alternatively: Buy and Generate a Shipping Label all in One API Call\n\nIf you already know which rate you want, you can skip the separate buy step entirely by combining Shipment creation and label purchase into a single request. Pass the `service` you want (for example, `First`) at the top level of the Shipment, and set `options.create_and_buy` to `true`. Vanlo will create the Shipment, pick the rate that matches the requested service, and purchase the label in one round trip.\n\n```shell\ncurl -X POST https://www.vanlo.com/api/v1/shipments \\\n  -H 'Authorization: Bearer VANLO_API_KEY' \\\n  -H 'Content-Type: application/json' \\\n  -d '{\n  \"shipment\": {\n    \"to_address\":   { \"id\": \"adr_...\" },\n    \"from_address\": { \"id\": \"adr_...\" },\n    \"parcel\":       { \"id\": \"prcl_...\" },\n    \"service\": \"First\",\n    \"options\": {\n      \"create_and_buy\": true\n    }\n  }\n}'\n```\n\n```ruby\nrequire 'vanlo'\nVanlo.api_key = 'VANLO_API_KEY'\n\nVanlo::Shipment.create(\n  to_address:   { id: \"adr_...\" },\n  from_address: { id: \"adr_...\" },\n  parcel:       { id: \"prcl_...\" },\n  service: \"First\",\n  options: { create_and_buy: true }\n)\n```\n\n```python\nimport vanlo\nvanlo.api_key = 'VANLO_API_KEY'\n\nvanlo.Shipment.create(\n  to_address={ \"id\": \"adr_...\" },\n  from_address={ \"id\": \"adr_...\" },\n  parcel={ \"id\": \"prcl_...\" },\n  service=\"First\",\n  options={ \"create_and_buy\": True }\n)\n```\n\n```php\nrequire_once(\"/path/to/lib/vanlo.php\");\n\\Vanlo\\Vanlo::setApiKey('VANLO_API_KEY');\n\n$shipment = \\Vanlo\\Shipment::create(array(\n  \"to_address\"   => array(\"id\" => \"adr_...\"),\n  \"from_address\" => array(\"id\" => \"adr_...\"),\n  \"parcel\"       => array(\"id\" => \"prcl_...\"),\n  \"service\"      => \"First\",\n  \"options\"      => array(\"create_and_buy\" => true)\n));\n```\n\n```csharp\nusing Vanlo;\nClientManager.SetCurrent(\"VANLO_API_KEY\");\n\nShipment shipment = Shipment.Create(new Dictionary<string, object>() {\n    { \"to_address\",   new Dictionary<string, object>() { { \"id\", \"adr_...\" } } },\n    { \"from_address\", new Dictionary<string, object>() { { \"id\", \"adr_...\" } } },\n    { \"parcel\",       new Dictionary<string, object>() { { \"id\", \"prcl_...\" } } },\n    { \"service\", \"First\" },\n    { \"options\",\n      new Dictionary<string, object>() { { \"create_and_buy\", true } }\n    }\n});\n```\n\n> The above command returns JSON structured like this:\n\n```json\n{\n  \"id\": \"shp_...\",\n  \"object\": \"Shipment\",\n  \"tracking_code\": \"9499907123456123456781\",\n  \"postage_label\": {\n    \"id\": \"pl_...\",\n    \"object\": \"PostageLabel\",\n    \"label_url\": \"https://amazonaws.com/.../a1b2c3.png\",\n    \"label_file_type\": \"image/png\"\n  },\n  \"selected_rate\": {\n    \"id\": \"rate_...\",\n    \"service\": \"First\",\n    \"rate\": \"2.93\",\n    \"carrier\": \"USPS\"\n  },\n  \"tracker\": {\n    \"id\": \"trk_...\",\n    \"object\": \"Tracker\"\n  }\n}\n```\n\nThe response is the same shape as the standard buy-shipment flow - you get a fully created Shipment with a `postage_label.label_url` you can print immediately, plus the Tracker that Vanlo automatically starts for you.\n\n### HTTP Request\n\n`POST https://www.vanlo.com/api/v1/shipments`\n\n### Parameters\n\nParameter | Type | Specification\n--------- | ---- | -------------\nto_address | object | Destination Address (or `{ \"id\": \"adr_...\" }` for an existing address)\nfrom_address | object | Origin Address (or `{ \"id\": \"adr_...\" }` for an existing address)\nparcel | object | Parcel being shipped (or `{ \"id\": \"prcl_...\" }` for an existing parcel)\nservice | string | The carrier service to purchase (e.g. `First`, `Priority`, `Express`, `GroundAdvantage`)\noptions.create_and_buy | boolean | Set to `true` to create the Shipment and purchase the label in a single request\n\n<div class=\"guides-related-cards\">\n  <a class=\"guides-related-card\" href=\"#\" data-guide-switch=\"tracking\">\n    <div class=\"guides-related-card__text\">\n      <span class=\"guides-related-card__label\">RELATED LINK</span>\n      <span class=\"guides-related-card__title\">Tracking Guide</span>\n    </div>\n    <svg class=\"guides-related-card__icon\" width=\"17\" height=\"17\" viewBox=\"0 0 17 17\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\"><path d=\"M8.25 11.25L11.25 8.25M11.25 8.25L8.25 5.25M11.25 8.25H5.25M15.75 8.25C15.75 12.3921 12.3921 15.75 8.25 15.75C4.10786 15.75 0.75 12.3921 0.75 8.25C0.75 4.10786 4.10786 0.75 8.25 0.75C12.3921 0.75 15.75 4.10786 15.75 8.25Z\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/></svg>\n  </a>\n  <a class=\"guides-related-card\" href=\"#\" data-guide-switch=\"webhooks\">\n    <div class=\"guides-related-card__text\">\n      <span class=\"guides-related-card__label\">RELATED LINK</span>\n      <span class=\"guides-related-card__title\">Webhooks Guide</span>\n    </div>\n    <svg class=\"guides-related-card__icon\" width=\"17\" height=\"17\" viewBox=\"0 0 17 17\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\"><path d=\"M8.25 11.25L11.25 8.25M11.25 8.25L8.25 5.25M11.25 8.25H5.25M15.75 8.25C15.75 12.3921 12.3921 15.75 8.25 15.75C4.10786 15.75 0.75 12.3921 0.75 8.25C0.75 4.10786 4.10786 0.75 8.25 0.75C12.3921 0.75 15.75 4.10786 15.75 8.25Z\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/></svg>\n  </a>\n</div>"
    },
    {
      "id": "reports",
      "title": "Reports Guide",
      "content": "# Reports Guide\n\nThis guide will walk you through generating and downloading Reports via the Vanlo API. In this example, we'll step into the shoes of an ops team at Vandelay Industries that needs to reconcile last month's shipments against the carrier invoice. We'll generate a January shipment report, wait for it to finish, download the CSV, and handle a few of the errors you'll run into along the way.\n\nA Report is an asynchronously-generated CSV export of a slice of your account activity - shipments, trackers, refunds, invoices, balances - scoped to a date range you choose. Reports are perfect for monthly reconciliation, finance exports, and feeding data into your own analytics stack.\n\n<p class=\"guides-label\">Before You Start</p>\n\n<div class=\"guides-info\">\n  <p>Make sure you have a Vanlo API key. For your first run we recommend Test Mode credentials from the <a href=\"https://dashboard.vanlo.com/apikey\">API Keys tab under the Developers Menu</a>.</p>\n  <p>Skim the <a href=\"/#reports\">Reports API reference</a> for the full parameter list and object shape.</p>\n  <p>Report generation is asynchronous. Vanlo does not emit webhook events for the report lifecycle - your two notification options are polling and email, both covered in Step 2.</p>\n  <p>Date ranges are capped at 31 days per report. Chunk larger exports into multiple calls.</p>\n  <p>Test Mode and Live Mode reports are isolated. Use the API key that matches the environment you care about - a test-mode key will never return live-mode shipments, and vice versa.</p>\n  <p>Vanlo supports 8 report types. For this walkthrough we'll use <code>shipment</code> (the richest, 45 columns); see the Column Reference at the end of this guide for all 8 types and the full column lists.</p>\n</div>\n\n\n## Step 1: Create a Report\n\nDecide on a date range and POST to `/api/v1/reports/:type`, where `:type` is one of: `shipment`, `tracking`, `refund`, `payment_log`, `shipment_invoice`, `invoice_item`, `balance_snapshot`, or `fedex_detail`. For our reconciliation example, we'll create a shipment report for January 2024.\n\n\n### Create Shipment Report\n\n```shell\ncurl -X POST https://www.vanlo.com/api/v1/reports/shipment \\\n  -H 'Authorization: Bearer VANLO_API_KEY' \\\n  -d 'start_date=2024-01-01' \\\n  -d 'end_date=2024-01-31'\n```\n\n```ruby\nrequire 'vanlo'\nVanlo.api_key = 'VANLO_API_KEY'\n\nVanlo::Report.create(\n  type: 'shipment',\n  start_date: '2024-01-01',\n  end_date: '2024-01-31'\n)\n```\n\n```python\nimport vanlo\nvanlo.api_key = 'VANLO_API_KEY'\n\nvanlo.Report.create(\n  type='shipment',\n  start_date='2024-01-01',\n  end_date='2024-01-31'\n)\n```\n\n```php\nrequire_once('/path/to/lib/vanlo.php');\n\\Vanlo\\Vanlo::setApiKey('VANLO_API_KEY');\n\n$report = \\Vanlo\\Report::create(array(\n  'type'       => 'shipment',\n  'start_date' => '2024-01-01',\n  'end_date'   => '2024-01-31'\n));\n```\n\n```csharp\nusing Vanlo;\nClientManager.SetCurrent(\"VANLO_API_KEY\");\n\nReport report = Report.Create(new Dictionary<string, object>() {\n    { \"type\",       \"shipment\" },\n    { \"start_date\", \"2024-01-01\" },\n    { \"end_date\",   \"2024-01-31\" }\n});\n```\n\n\n### Create Shipment Report Response\n\n```json\n{\n  \"id\": \"shprep_...\",\n  \"object\": \"ShipmentReport\",\n  \"created_at\": \"2024-02-01T12:00:00Z\",\n  \"updated_at\": \"2024-02-01T12:00:00Z\",\n  \"start_date\": \"2024-01-01\",\n  \"end_date\": \"2024-01-31\",\n  \"status\": \"new\",\n  \"url\": null,\n  \"url_expires_at\": null\n}\n```\n\nThe initial response always comes back with `status: \"new\"` and `url: null`. Report generation runs in the background - the response confirms the job was queued, not that the CSV is ready. Don't try to use the `url` field yet; it's populated later when the worker finishes.\n\n**Date defaults.** If you omit one or both dates, Vanlo fills them in for you:\n\n- Omit `start_date` - defaults to `end_date - 31 days`\n- Omit `end_date` - defaults to `start_date + 31 days`\n- Omit both - defaults to the last 31 days ending today\n\nThe 31-day maximum applies to the final computed range, not just the values you pass in. Exceeding it returns an HTTP 400 error - see Step 5 for the exact response shape and how to recover.\n\n**Date formats.** The API accepts `YYYY-MM-DD` (e.g., `2024-01-01`) and `MM/DD/YYYY` (e.g., `01/01/2024`). We recommend sticking with ISO `YYYY-MM-DD` for portability across client libraries.\n\n**Optional parameters on create:**\n\nParameter | Type | Purpose\n--------- | ---- | -------\nstart_date | date | Start of the report range (inclusive)\nend_date | date | End of the report range (inclusive)\nsend_email | boolean | If true, email the download link when the report is ready. See Step 2 Option B\nsend_email_address | string | Override the notification email. Defaults to the account email\n\n\n## Step 2: Wait for the Report to Finish\n\nA freshly-created report moves through these statuses:\n\nStatus | Meaning\n------ | -------\nnew | Queued - no worker has picked it up yet\ngenerating | A background worker is building the CSV\nready | CSV is zipped and uploaded; `url` is populated\nerror | Generation failed - the report will not retry automatically\n\n**Reports do not emit webhook events.** The shared webhook system supports events like `tracker.updated`, `batch.completed`, and `refund.successful`, but nothing for the report lifecycle. Your two options for finding out when a report is done are polling and email notification. Pick one based on how your application is structured.\n\n\n### Option A: Poll the Report\n\nRetrieve the report by ID every few seconds until `status` is `ready` or `error`. Most shipment reports finish in under a minute, but larger date ranges or busier accounts can take longer. We recommend polling at most once every 5 seconds and capping total wait time at a few minutes.\n\n```shell\ncurl -X GET https://www.vanlo.com/api/v1/reports/shprep_... \\\n  -H 'Authorization: Bearer VANLO_API_KEY'\n```\n\n```ruby\nrequire 'vanlo'\nVanlo.api_key = 'VANLO_API_KEY'\n\nVanlo::Report.retrieve('shprep_...')\n```\n\n```python\nimport vanlo\nvanlo.api_key = 'VANLO_API_KEY'\n\nvanlo.Report.retrieve('shprep_...')\n```\n\n```php\nrequire_once('/path/to/lib/vanlo.php');\n\\Vanlo\\Vanlo::setApiKey('VANLO_API_KEY');\n\n$report = \\Vanlo\\Report::retrieve('shprep_...');\n```\n\n```csharp\nusing Vanlo;\nClientManager.SetCurrent(\"VANLO_API_KEY\");\n\nReport report = Report.Retrieve(\"shprep_...\");\n```\n\n\n### Poll Response (Ready)\n\n```json\n{\n  \"id\": \"shprep_...\",\n  \"object\": \"ShipmentReport\",\n  \"created_at\": \"2024-02-01T12:00:00Z\",\n  \"updated_at\": \"2024-02-01T12:05:00Z\",\n  \"start_date\": \"2024-01-01\",\n  \"end_date\": \"2024-01-31\",\n  \"status\": \"ready\",\n  \"url\": \"https://vanlo-files.s3.amazonaws.com/files/reports/shipments-2024-01-01-2024-01-31.zip\",\n  \"url_expires_at\": \"2024-02-01T13:05:00Z\"\n}\n```\n\n\n### Option B: Email Notification\n\nPass `send_email=true` on creation. When the report transitions to `ready`, Vanlo emails the download link to the account email, or to a custom address if you pass `send_email_address`.\n\n```shell\ncurl -X POST https://www.vanlo.com/api/v1/reports/shipment \\\n  -H 'Authorization: Bearer VANLO_API_KEY' \\\n  -d 'start_date=2024-01-01' \\\n  -d 'end_date=2024-01-31' \\\n  -d 'send_email=true' \\\n  -d 'send_email_address=ops@vandelayindustries.com'\n```\n\n```ruby\nrequire 'vanlo'\nVanlo.api_key = 'VANLO_API_KEY'\n\nVanlo::Report.create(\n  type: 'shipment',\n  start_date: '2024-01-01',\n  end_date: '2024-01-31',\n  send_email: true,\n  send_email_address: 'ops@vandelayindustries.com'\n)\n```\n\n```python\nimport vanlo\nvanlo.api_key = 'VANLO_API_KEY'\n\nvanlo.Report.create(\n  type='shipment',\n  start_date='2024-01-01',\n  end_date='2024-01-31',\n  send_email=True,\n  send_email_address='ops@vandelayindustries.com'\n)\n```\n\n```php\nrequire_once('/path/to/lib/vanlo.php');\n\\Vanlo\\Vanlo::setApiKey('VANLO_API_KEY');\n\n$report = \\Vanlo\\Report::create(array(\n  'type'               => 'shipment',\n  'start_date'         => '2024-01-01',\n  'end_date'           => '2024-01-31',\n  'send_email'         => true,\n  'send_email_address' => 'ops@vandelayindustries.com'\n));\n```\n\n```csharp\nusing Vanlo;\nClientManager.SetCurrent(\"VANLO_API_KEY\");\n\nReport report = Report.Create(new Dictionary<string, object>() {\n    { \"type\",               \"shipment\" },\n    { \"start_date\",         \"2024-01-01\" },\n    { \"end_date\",           \"2024-01-31\" },\n    { \"send_email\",         true },\n    { \"send_email_address\", \"ops@vandelayindustries.com\" }\n});\n```\n\n**Important:** the email is only sent on success. If generation fails and the report lands in `status: \"error\"`, you will not receive any notification - you'll need to poll or check the dashboard to catch errors. For critical workflows, we recommend combining email with a lightweight periodic sanity check.\n\n\n## Step 3: Download the CSV\n\nOnce `status` is `ready`, the `url` field is a pre-signed S3 link to a ZIP file containing a single CSV. You can download it with any HTTP client - no authentication header is needed because the URL is already signed.\n\n```shell\ncurl -L -o shipment-report.zip \\\n  \"https://vanlo-files.s3.amazonaws.com/files/reports/shipments-2024-01-01-2024-01-31.zip\"\n```\n\n```ruby\nrequire 'open-uri'\n\nURI.open('https://vanlo-files.s3.amazonaws.com/files/reports/shipments-2024-01-01-2024-01-31.zip') do |remote|\n  File.open('shipment-report.zip', 'wb') { |f| f.write(remote.read) }\nend\n```\n\n```python\nimport urllib.request\n\nurllib.request.urlretrieve(\n  'https://vanlo-files.s3.amazonaws.com/files/reports/shipments-2024-01-01-2024-01-31.zip',\n  'shipment-report.zip'\n)\n```\n\n```php\nfile_put_contents(\n  'shipment-report.zip',\n  file_get_contents('https://vanlo-files.s3.amazonaws.com/files/reports/shipments-2024-01-01-2024-01-31.zip')\n);\n```\n\n```csharp\nusing System.Net.Http;\n\nusing var client = new HttpClient();\nvar bytes = await client.GetByteArrayAsync(\n    \"https://vanlo-files.s3.amazonaws.com/files/reports/shipments-2024-01-01-2024-01-31.zip\"\n);\nawait File.WriteAllBytesAsync(\"shipment-report.zip\", bytes);\n```\n\n**URL expiry.** The signed URL is good for 1 hour, anchored to the moment the report transitioned to `ready` (see the `url_expires_at` field), not to when you request the download. That means if you queued a report at 12:00 and didn't check email until 14:00, the link is already dead. Two things to know:\n\n1. The CSV itself is not deleted - only the signed URL expired.\n2. Retrieving the report again by ID (`GET /api/v1/reports/:id`) returns a fresh signed URL with a new `url_expires_at`. Re-retrieving does not re-generate the CSV.\n\n```shell\ncurl -X GET https://www.vanlo.com/api/v1/reports/shprep_... \\\n  -H 'Authorization: Bearer VANLO_API_KEY'\n```\n\n```ruby\nrequire 'vanlo'\nVanlo.api_key = 'VANLO_API_KEY'\n\nVanlo::Report.retrieve('shprep_...')\n```\n\n```python\nimport vanlo\nvanlo.api_key = 'VANLO_API_KEY'\n\nvanlo.Report.retrieve('shprep_...')\n```\n\n```php\nrequire_once('/path/to/lib/vanlo.php');\n\\Vanlo\\Vanlo::setApiKey('VANLO_API_KEY');\n\n$report = \\Vanlo\\Report::retrieve('shprep_...');\n```\n\n```csharp\nusing Vanlo;\nClientManager.SetCurrent(\"VANLO_API_KEY\");\n\nReport report = Report.Retrieve(\"shprep_...\");\n```\n\nUse the new `url` from that response to complete the download.\n\n\n## Step 4: List Historical Reports\n\nTo browse reports you've previously generated, list them by type. The response is paginated.\n\n\n### List Shipment Reports\n\n```shell\ncurl -X GET https://www.vanlo.com/api/v1/reports/shipment \\\n  -H 'Authorization: Bearer VANLO_API_KEY' \\\n  -d 'page_size=2'\n```\n\n```ruby\nrequire 'vanlo'\nVanlo.api_key = 'VANLO_API_KEY'\n\nVanlo::Report.all(type: 'shipment', page_size: 2)\n```\n\n```python\nimport vanlo\nvanlo.api_key = 'VANLO_API_KEY'\n\nvanlo.Report.all(type='shipment', page_size=2)\n```\n\n```php\nrequire_once('/path/to/lib/vanlo.php');\n\\Vanlo\\Vanlo::setApiKey('VANLO_API_KEY');\n\n$reports = \\Vanlo\\Report::all(array(\n  'type'      => 'shipment',\n  'page_size' => 2\n));\n```\n\n```csharp\nusing Vanlo;\nClientManager.SetCurrent(\"VANLO_API_KEY\");\n\nReportList reportList = Report.List(new Dictionary<string, object>() {\n    { \"type\",      \"shipment\" },\n    { \"page_size\", 2 }\n});\n```\n\n\n### List Response\n\n```json\n{\n  \"reports\": [\n    {\n      \"id\": \"shprep_abc123\",\n      \"object\": \"ShipmentReport\",\n      \"created_at\": \"2024-02-01T12:00:00Z\",\n      \"updated_at\": \"2024-02-01T12:05:00Z\",\n      \"start_date\": \"2024-01-01\",\n      \"end_date\": \"2024-01-31\",\n      \"status\": \"ready\",\n      \"url\": \"https://vanlo-files.s3.amazonaws.com/files/reports/shipments-2024-01-01-2024-01-31.zip\",\n      \"url_expires_at\": \"2024-02-01T13:05:00Z\"\n    },\n    {\n      \"id\": \"shprep_def456\",\n      \"object\": \"ShipmentReport\",\n      \"created_at\": \"2024-01-15T08:30:00Z\",\n      \"updated_at\": \"2024-01-15T08:35:00Z\",\n      \"start_date\": \"2023-12-15\",\n      \"end_date\": \"2024-01-14\",\n      \"status\": \"ready\",\n      \"url\": \"https://vanlo-files.s3.amazonaws.com/files/reports/shipments-2023-12-15-2024-01-14.zip\",\n      \"url_expires_at\": \"2024-02-01T13:05:00Z\"\n    }\n  ],\n  \"has_more\": true\n}\n```\n\nThe `has_more` flag tells you whether more pages are available. To fetch the next page, pass the ID of the last report you received as the `after_id` parameter:\n\n\n### List Next Page\n\n```shell\ncurl -X GET https://www.vanlo.com/api/v1/reports/shipment \\\n  -H 'Authorization: Bearer VANLO_API_KEY' \\\n  -d 'page_size=2' \\\n  -d 'after_id=shprep_def456'\n```\n\n```ruby\nrequire 'vanlo'\nVanlo.api_key = 'VANLO_API_KEY'\n\nVanlo::Report.all(\n  type: 'shipment',\n  page_size: 2,\n  after_id: 'shprep_def456'\n)\n```\n\n```python\nimport vanlo\nvanlo.api_key = 'VANLO_API_KEY'\n\nvanlo.Report.all(\n  type='shipment',\n  page_size=2,\n  after_id='shprep_def456'\n)\n```\n\n```php\nrequire_once('/path/to/lib/vanlo.php');\n\\Vanlo\\Vanlo::setApiKey('VANLO_API_KEY');\n\n$reports = \\Vanlo\\Report::all(array(\n  'type'      => 'shipment',\n  'page_size' => 2,\n  'after_id'  => 'shprep_def456'\n));\n```\n\n```csharp\nusing Vanlo;\nClientManager.SetCurrent(\"VANLO_API_KEY\");\n\nReportList reportList = Report.List(new Dictionary<string, object>() {\n    { \"type\",      \"shipment\" },\n    { \"page_size\", 2 },\n    { \"after_id\",  \"shprep_def456\" }\n});\n```\n\nThe `start_datetime` / `end_datetime` parameters filter by when each report was created - not by the date range inside the report.\n\n\n## Step 5: Handle Errors\n\nTwo error paths are worth handling explicitly in your integration.\n\n\n### Rejected on Create (HTTP 400)\n\nIf you pass an invalid date range, the API rejects the request synchronously with HTTP 400. Here's what you get when you exceed the 31-day cap:\n\n```shell\ncurl -X POST https://www.vanlo.com/api/v1/reports/shipment \\\n  -H 'Authorization: Bearer VANLO_API_KEY' \\\n  -d 'start_date=2024-01-01' \\\n  -d 'end_date=2024-03-15'\n```\n\n```ruby\nrequire 'vanlo'\nVanlo.api_key = 'VANLO_API_KEY'\n\nbegin\n  Vanlo::Report.create(\n    type: 'shipment',\n    start_date: '2024-01-01',\n    end_date: '2024-03-15'\n  )\nrescue Vanlo::Error => e\n  puts e.code, e.message\nend\n```\n\n```python\nimport vanlo\nvanlo.api_key = 'VANLO_API_KEY'\n\ntry:\n  vanlo.Report.create(\n    type='shipment',\n    start_date='2024-01-01',\n    end_date='2024-03-15'\n  )\nexcept vanlo.Error as e:\n  print(e.code, e.message)\n```\n\n```php\nrequire_once('/path/to/lib/vanlo.php');\n\\Vanlo\\Vanlo::setApiKey('VANLO_API_KEY');\n\ntry {\n  \\Vanlo\\Report::create(array(\n    'type'       => 'shipment',\n    'start_date' => '2024-01-01',\n    'end_date'   => '2024-03-15'\n  ));\n} catch (\\Vanlo\\Error $e) {\n  echo $e->getCode() . ': ' . $e->getMessage();\n}\n```\n\n```csharp\nusing Vanlo;\nClientManager.SetCurrent(\"VANLO_API_KEY\");\n\ntry\n{\n    Report.Create(new Dictionary<string, object>() {\n        { \"type\",       \"shipment\" },\n        { \"start_date\", \"2024-01-01\" },\n        { \"end_date\",   \"2024-03-15\" }\n    });\n}\ncatch (VanloException e)\n{\n    Console.WriteLine($\"{e.Code}: {e.Message}\");\n}\n```\n\n\n### Error Response (HTTP 400)\n\n```json\n{\n  \"error\": {\n    \"code\": \"BAD_REQUEST\",\n    \"message\": \"Invalid parameters were passed and the record could not be persisted. Request ID: 4f2d1e8a-...\",\n    \"errors\": [\n      {\n        \"field\": \"end_date\",\n        \"message\": \"The date range covers more than 31 days\"\n      }\n    ]\n  }\n}\n```\n\nThe `error.code` field is stable and safe to match on; the `error.message` is human-readable. The `errors` array breaks each validation failure down to a specific field, which is useful if you surface these messages in your own UI.\n\n\n### Async Failure (status: \"error\")\n\nIf the background worker fails partway through generation, the report ends up in `status: \"error\"`. Unlike the 400 above, this is not returned synchronously - you'll only see it when you poll the report by ID.\n\n```json\n{\n  \"id\": \"shprep_...\",\n  \"object\": \"ShipmentReport\",\n  \"created_at\": \"2024-02-01T12:00:00Z\",\n  \"updated_at\": \"2024-02-01T12:02:00Z\",\n  \"start_date\": \"2024-01-01\",\n  \"end_date\": \"2024-01-31\",\n  \"status\": \"error\",\n  \"url\": null,\n  \"url_expires_at\": null\n}\n```\n\nImportant: the `error` status has no accompanying message field on the report object, and email notification is **not** sent for failures. If your workflow depends on catching errors, you must poll. Recovery is simple: POST a new create request. Vanlo does not automatically retry failed reports.\n\nCongratulations! You've just generated, polled, and downloaded your first Vanlo Report. Head to the <a href=\"/#reports\">Reports reference</a> for the full parameter catalog.\n\n<div class=\"guides-related-cards\">\n  <a class=\"guides-related-card\" href=\"/#reports\">\n    <div class=\"guides-related-card__text\">\n      <span class=\"guides-related-card__label\">RELATED LINK</span>\n      <span class=\"guides-related-card__title\">Reports Reference</span>\n    </div>\n    <svg class=\"guides-related-card__icon\" width=\"17\" height=\"17\" viewBox=\"0 0 17 17\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\"><path d=\"M8.25 11.25L11.25 8.25M11.25 8.25L8.25 5.25M11.25 8.25H5.25M15.75 8.25C15.75 12.3921 12.3921 15.75 8.25 15.75C4.10786 15.75 0.75 12.3921 0.75 8.25C0.75 4.10786 4.10786 0.75 8.25 0.75C12.3921 0.75 15.75 4.10786 15.75 8.25Z\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/></svg>\n  </a>\n</div>\n\n\n## Column Reference\n\nThe exact columns in the CSV depend on the report type. All 8 types, in order of how commonly they're requested:\n\n\n### Shipment Report (45 columns)\n\nEvery shipment in the range, flattened into a single row per shipment with from/to address, parcel dimensions, service, carrier, rate, fees, and refund status. Most commonly used for monthly reconciliation.\n\n```text\ncreated_at, id, tracking_code, status, from_address_id, from_name,\nfrom_company, from_street1, from_street2, from_city, from_state,\nfrom_zip, from_country, from_residential, to_address_id, to_name,\nto_company, to_street1, to_street2, to_city, to_state, to_zip,\nto_country, to_residential, parcel_id, length, width, height, weight,\npredefined_package, postage_label_created_at, rate_id, service, carrier,\nrate, insured_value, is_return, refund_status, reference, label_fee,\npostage_fee, insurance_fee, options, rate_details, dim_weight\n```\n\n`rate_details` is the rate broken into its parts: the transport charge, the extra fees added on top, and their total. Each carrier names those entries its own way, and USPS names them with codes. The <a href=\"/#rate-object\">Rate Object reference</a> lists the USPS codes and what triggers each fee.\n\n`dim_weight` is the billed dimensional weight in ounces, appended as the last column so existing column positions are unchanged. It is filled only for rates Vanlo prices itself against a dimensional divisor - USPS, OSM, UniUni and International Bridge - and only when that rate was actually billed on dimensional weight rather than actual weight or cubic pricing. It is always empty for UPS and FedEx, whose rates report a single billing weight without saying whether it came from the dimensions, so an empty value there does not mean dim weight was not charged.\n\n\n### Tracking Report (11 columns)\n\nCompact view of every tracker in the range. Useful for delivery SLA reporting and customer support dashboards.\n\n```text\ntracking_code, service, status, origin_state, origin_zip, dest_state,\ndest_zip, print_custom_1, label_date, in_transit_datetime, delivered_datetime\n```\n\n\n### Refund Report (12 columns)\n\nEvery shipment that had a refund initiated in the range, with the full fee breakdown and current refund status. Best for finance reconciliation.\n\n```text\nshipment_created_at, shipment_id, utc_offset, postage_fee, label_fee,\ninsurance_fee, tracker_fee, fulfillment_fee, total_fees, refund_amount,\nrefund_status, refunded_at\n```\n\n\n### Payment Log Report (10 columns)\n\nMoney movements between your account and Vanlo - top-ups, charges, refunds. Use this for balance reconciliation.\n\n```text\ncreated_at, id, status, source_type, target_type, charge_type,\ntracking_code, amount, balance, usps_cubic\n```\n\n\n### Shipment Invoice Report (32 columns)\n\nReconciles carrier billing against what you were initially charged. Surfaces claimed-vs-captured dimensions, service levels, and any adjustment amounts. Essential if you're disputing carrier invoices.\n\n```text\nshipment_id, label_date, carrier_account_id, carrier, tracking_code,\ncarrier_invoice_id, package_dispute_id, status, quoted_currency,\ninitially_paid_amount, quoted_amount, claimed_length, claimed_width,\nclaimed_height, claimed_weight, claimed_package, claimed_service,\nfinal_invoice_amount, captured_length, captured_width, captured_height,\ncaptured_weight, captured_package, captured_service, captured_currency,\nadjustment_amount, adjustment_reason, invoice_date,\ninitially_paid_payment_log, invoice_payment_log, user_id, user_parent_id\n```\n\n\n### Invoice Item Report (17 columns)\n\nLine-item breakdown per invoice, including incentive ratios and shipment dimensions. Useful for per-shipment cost analysis.\n\n```text\ncreated_at, id, source_type, target_type, charge_type, amount,\nincentive_ratio, incentive_amount, shipment_id, weight, length, width,\nheight, package, usps_cubic, usps_zone, dim_weight\n```\n\n\n### Balance Snapshot Report (2 columns)\n\nDaily end-of-day available balance. Minimal by design - pair it with Payment Log for a full finance picture.\n\n```text\ndate, end_of_date_balance\n```\n\n\n### FedEx Detail Report (31 columns)\n\nFedEx-specific breakdown comparing quoted vs final billing weights and surcharges (fuel, DAS, other). Use this when auditing FedEx shipments.\n\n```text\ncreated_at, id, tracking_code, reference, carrier, service, from_zip,\nfrom_country, to_zip, to_country, zone, length, width, height,\npredefined_package, weight, quote_billing_weight, quote_rate, quote_freight,\nquote_surcharges, quote_surcharge_fuel, quote_surcharge_das,\nquote_surcharge_other, final_billing_weight, final_rate, final_freight,\nfinal_surcharges, final_surcharge_fuel, final_surcharge_das,\nfinal_surcharge_other, dim_weight\n```\n\n\n## Reference: All Report Types\n\nType | Object Name | ID Prefix\n---- | ----------- | ---------\nshipment | ShipmentReport | shprep_\npayment_log | PaymentLogReport | plrep_\nshipment_invoice | ShipmentInvoiceReport | shpinvrep_\nrefund | RefundReport | refrep_\ntracking | TrackingReport | trkrep_\ninvoice_item | InvoiceItemReport | report_\nbalance_snapshot | BalanceSnapshotReport | report_\nfedex_detail | FedExDetailReport | fdxrep_"
    },
    {
      "id": "tracking",
      "title": "Tracking Guide",
      "content": "# Tracking Guide\n\nThis guide will teach you the two ways to track packages with Vanlo.\n\nThere's two methods of tracking packages with Vanlo:\n\nProvide your existing tracking number and carrier.\n\nPurchase the shipping label with Vanlo, which automatically generates a <a href=\"/#trackers\">Tracker</a>.\n\nOur tracking service uses webhooks to give you updates about your shipments. Refer to our <a href=\"#\" data-guide-switch=\"webhooks\">Webhooks Guide</a> to learn how we use webhooks to send you tracking updates.\n\n<p class=\"guides-label\">Before You Start</p>\n\n<div class=\"guides-info\">\n  <p><a href=\"https://dashboard.vanlo.com/\">Log in to an existing account.</a></p>\n  <p><a href=\"http://dashboard.vanlo.com/webhooks\">Setup your webhook URLs</a> for Test Mode and Production Mode.</p>\n  <p>Grab one of our <a href=\"https://github.com/VanloCorp\">official client libraries</a>.</p>\n  <p>Choose the \"Step 1\" that is applicable to you:</p>\n  <p>I have a tracking number.</p>\n  <p>I want to buy a shipping label with a tracker.</p>\n  <p>If you haven't run through our <a href=\"#\" data-guide-switch=\"getting-started\">Getting Started Guide</a>, definitely do that before moving on to this one.</p>\n</div>\n\n<div class=\"guides-related-cards\">\n  <a class=\"guides-related-card\" href=\"#\" data-guide-switch=\"webhooks\">\n    <div class=\"guides-related-card__text\">\n      <span class=\"guides-related-card__label\">RELATED LINK</span>\n      <span class=\"guides-related-card__title\">Webhooks Guide</span>\n    </div>\n    <svg class=\"guides-related-card__icon\" width=\"17\" height=\"17\" viewBox=\"0 0 17 17\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\"><path d=\"M8.25 11.25L11.25 8.25M11.25 8.25L8.25 5.25M11.25 8.25H5.25M15.75 8.25C15.75 12.3921 12.3921 15.75 8.25 15.75C4.10786 15.75 0.75 12.3921 0.75 8.25C0.75 4.10786 4.10786 0.75 8.25 0.75C12.3921 0.75 15.75 4.10786 15.75 8.25Z\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/></svg>\n  </a>\n</div>\n\n\n## Step 1: Create a Tracker using your tracking code & carrier\n\nTo track a shipment not created with Vanlo, you simply need to create a <a href=\"/#tracker-object\">Tracker Object</a>. The only required field is `tracking_code`. The public API always auto-detects the carrier from the tracking code via the downstream tracking backend - you may include a `carrier` field in the request for readability, but the API ignores it. If the downstream backend cannot match the code to a specific carrier, Vanlo returns a `422 Unprocessable Entity` response with a descriptive error and the Tracker is not created.\n\nHere is an example of how to make a tracker object:\n\n\n### Creating a Tracker\n\n```shell\ncurl -X POST https://www.vanlo.com/api/v1/trackers \\\n  -H 'Authorization: Bearer VANLO_API_KEY' \\\n  -H 'Content-Type: application/json' \\\n  -d '{\n  \"tracker\": {\n    \"tracking_code\": \"9400110898825022579493\",\n    \"carrier\": \"USPS\"\n  }\n}'\n```\n\n```ruby\nrequire 'vanlo'\nVanlo.api_key = 'VANLO_API_KEY'\n\nVanlo::Tracker.create(\n  tracking_code: '9400110898825022579493',\n  carrier: 'USPS'\n)\n```\n\n```python\nimport vanlo\nvanlo.api_key = 'VANLO_API_KEY'\n\nvanlo.Tracker.create(\n  tracking_code='9400110898825022579493',\n  carrier='USPS'\n)\n```\n\n```php\nrequire_once('/path/to/lib/vanlo.php');\n\\Vanlo\\Vanlo::setApiKey('VANLO_API_KEY');\n\n$tracker = \\Vanlo\\Tracker::create(array(\n  'tracking_code' => '9400110898825022579493',\n  'carrier'       => 'USPS'\n));\n```\n\n```csharp\nusing Vanlo;\nClientManager.SetCurrent(\"VANLO_API_KEY\");\n\nTracker tracker = Tracker.Create(\"USPS\", \"9400110898825022579493\");\n```\n\n\n### JSON from Response\n\n```json\n{\n  \"id\": \"trk_...\",\n  \"object\": \"Tracker\",\n  \"tracking_code\": \"EZ4000000004\",\n  \"status\": \"delivered\",\n  \"carrier\": \"UPS\",\n  \"signed_by\": \"John Tester\",\n  \"est_delivery_date\": \"2014-11-27T00:00:00Z\",\n  \"public_url\": \"https://track.vanlo.com/ajE7...\",\n  \"tracking_details\": [\n    {\n      \"object\": \"TrackingDetail\",\n      \"message\": \"BILLING INFORMATION RECEIVED\",\n      \"status\": \"pre_transit\",\n      \"datetime\": \"2014-11-21T14:24:00Z\",\n      \"tracking_location\": { \"city\": null, \"state\": null, \"country\": null }\n    }\n  ]\n}\n```\n\nAfter a Tracker is created, we will periodically check the status of your package and notify you when its status changes. <a href=\"#step-2-process-tracking-event-webhooks\">Jump to Step 2</a> to learn how we'll give you updates via webhooks.\n\n\n## Step 1: Purchase a Shipping Label and Tracking Code\n\nTo start tracking a package, there is nothing extra you need to do. Whenever you purchase a shipping label with Vanlo, we'll start automatically tracking its progress and notifying you of any updates via webhooks (more on that in <a href=\"#step-2-process-tracking-event-webhooks\">Step 2</a>).\n\nWhen you purchase a shipping label, we will also respond back with the tracking number for the label. You don't need to store the tracking number to get tracking updates but it is generally good practice to store it. Here's an example of purchasing a shipping label and getting a tracking number. To see all the steps for shipping a package, take a look at our <a href=\"#\" data-guide-switch=\"getting-started\">Getting Started Guide</a>.\n\n<div class=\"guides-related-cards\">\n  <a class=\"guides-related-card\" href=\"#\" data-guide-switch=\"getting-started\">\n    <div class=\"guides-related-card__text\">\n      <span class=\"guides-related-card__label\">RELATED LINK</span>\n      <span class=\"guides-related-card__title\">Getting Started Guide</span>\n    </div>\n    <svg class=\"guides-related-card__icon\" width=\"17\" height=\"17\" viewBox=\"0 0 17 17\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\"><path d=\"M8.25 11.25L11.25 8.25M11.25 8.25L8.25 5.25M11.25 8.25H5.25M15.75 8.25C15.75 12.3921 12.3921 15.75 8.25 15.75C4.10786 15.75 0.75 12.3921 0.75 8.25C0.75 4.10786 4.10786 0.75 8.25 0.75C12.3921 0.75 15.75 4.10786 15.75 8.25Z\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/></svg>\n  </a>\n</div>\n\n\nIf you want to add insurance, set `options.insurance_amount` when you **create** the Shipment - the `/buy` endpoint itself only accepts a `rate` selection.\n\n### Buying Shipment\n\n```shell\ncurl -X POST https://www.vanlo.com/api/v1/shipments/shp_.../buy \\\n  -H 'Authorization: Bearer VANLO_API_KEY' \\\n  -H 'Content-Type: application/json' \\\n  -d '{\n  \"rate\": {\n    \"id\": \"rate_...\"\n  }\n}'\n```\n\n```ruby\nrequire 'vanlo'\nVanlo.api_key = 'VANLO_API_KEY'\n\nshipment = Vanlo::Shipment.retrieve(\"shp_...\")\nshipment.buy(rate: shipment.lowest_rate)\n```\n\n```python\nimport vanlo\nvanlo.api_key = 'VANLO_API_KEY'\n\nshipment = vanlo.Shipment.retrieve(\"shp_...\")\nshipment.buy(rate=shipment.lowest_rate())\n```\n\n```php\nrequire_once(\"/path/to/lib/vanlo.php\");\n\\Vanlo\\Vanlo::setApiKey('VANLO_API_KEY');\n\n$shipment = \\Vanlo\\Shipment::retrieve(\"shp_...\");\n$shipment->buy(array(\n  'rate' => $shipment->lowest_rate()\n));\n```\n\n```csharp\nusing Vanlo;\nClientManager.SetCurrent(\"VANLO_API_KEY\");\n\nShipment shipment = Shipment.Retrieve(\"shp_...\");\nRate lowestRate = shipment.LowestRate();\n\nshipment.Buy(lowestRate);\n```\n\n\n### Using Results\n\n```json\n{\n  \"postage_label\": {\n    \"id\": \"pl_...\",\n    \"object\": \"PostageLabel\",\n    \"label_file_type\": \"image/png\",\n    \"label_url\": \"https://amazonaws.com/.../a1b2c3.png\"\n  },\n  \"tracking_code\": \"9499907123456123456781\",\n  \"selected_rate\": {\n    \"id\": \"rate_...\",\n    \"service\": \"First\",\n    \"rate\": \"2.25\",\n    \"carrier\": \"USPS\"\n  },\n  \"tracker\": { \"id\": \"trk_...\", \"object\": \"Tracker\" }\n}\n```\n\n\n## Step 2: Process Tracking Event Webhooks\n\nAfter you purchase a shipping label or create a tracker, we will automatically start sending tracking update <a href=\"/#events\">Events</a>. <a href=\"/#events\">Event Objects</a> are sent to the webhook URLs you've configured. Updates will be sent whenever there are new details associated with the tracker. We check the status of each package more frequently once it is Out for Delivery (out_for_delivery).\n\nThe Tracker Object statuses are listed in our <a href=\"/#tracker-object\">API Documentation</a>.\n\nWhen you purchase a label in either Test or Production mode, you will immediately get a tracking event after purchase. Test and Production mode behave slightly differently:\n\nIn Test Mode, tracker updates are simulated rather than driven by a real carrier scan feed. You should expect to receive at least one tracking event after the purchase so you can exercise your webhook handler end-to-end, but do not rely on Test Mode to replay the full carrier lifecycle that a production shipment would see.\n\nIn Production Mode, you will get your first of multiple tracking events for your package. The first tracking event is status `unknown`. The package stays in `unknown` until it is scanned by the carrier.\n\nYou'll know it is a tracking update event because the event's `object` field is `\"Event\"` and the `description` is `\"tracker.updated\"`. The `result` field contains the full <a href=\"/#tracker-object\">Tracker</a> object with the current progress of the package. When building application logic, the `status` attribute on the Tracker object is the most useful and reliable value to read.\n\nThe Tracker object also contains additional information from the carrier in \"tracking_details\" attribute. For example, along with \"in_transit\" updates you may receive information that it reached a particular location (eg \"Processed through Sort Facility June 01 2025 4:53 pm BELL GARDENS CA 9020\"). The \"tracking_details\" will be an array containing both details about the current status and all previous statuses. The oldest status is the first element of the array and we append newer statuses as they come in. Order is not perfectly reliable as carrier data is occasionally incorrect. We recommend using the \"status\" on the Tracker object for any key business logic.\n\n<div class=\"guides-related-cards\">\n  <a class=\"guides-related-card\" href=\"#tracker-object\">\n    <div class=\"guides-related-card__text\">\n      <span class=\"guides-related-card__label\">RELATED LINK</span>\n      <span class=\"guides-related-card__title\">Tracker Object</span>\n    </div>\n    <svg class=\"guides-related-card__icon\" width=\"17\" height=\"17\" viewBox=\"0 0 17 17\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\"><path d=\"M8.25 11.25L11.25 8.25M11.25 8.25L8.25 5.25M11.25 8.25H5.25M15.75 8.25C15.75 12.3921 12.3921 15.75 8.25 15.75C4.10786 15.75 0.75 12.3921 0.75 8.25C0.75 4.10786 4.10786 0.75 8.25 0.75C12.3921 0.75 15.75 4.10786 15.75 8.25Z\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/></svg>\n  </a>\n  <a class=\"guides-related-card\" href=\"#event-object\">\n    <div class=\"guides-related-card__text\">\n      <span class=\"guides-related-card__label\">RELATED LINK</span>\n      <span class=\"guides-related-card__title\">Event Object</span>\n    </div>\n    <svg class=\"guides-related-card__icon\" width=\"17\" height=\"17\" viewBox=\"0 0 17 17\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\"><path d=\"M8.25 11.25L11.25 8.25M11.25 8.25L8.25 5.25M11.25 8.25H5.25M15.75 8.25C15.75 12.3921 12.3921 15.75 8.25 15.75C4.10786 15.75 0.75 12.3921 0.75 8.25C0.75 4.10786 4.10786 0.75 8.25 0.75C12.3921 0.75 15.75 4.10786 15.75 8.25Z\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/></svg>\n  </a>\n</div>\n\nHere's an example of a tracking event webhook (we've trimmed the `tracking_details` list down to the first and last scan for readability):\n\n```json\n{\n  \"id\": \"evt_...\",\n  \"object\": \"Event\",\n  \"description\": \"tracker.updated\",\n  \"mode\": \"test\",\n  \"previous_attributes\": { \"status\": \"unknown\" },\n  \"result\": {\n    \"id\": \"trk_...\",\n    \"object\": \"Tracker\",\n    \"tracking_code\": \"EZ4000000004\",\n    \"status\": \"delivered\",\n    \"carrier\": \"UPS\",\n    \"signed_by\": \"John Tester\",\n    \"est_delivery_date\": \"2014-11-27T00:00:00Z\",\n    \"public_url\": \"https://track.vanlo.com/djE7...\",\n    \"tracking_details\": [\n      {\n        \"object\": \"TrackingDetail\",\n        \"message\": \"BILLING INFORMATION RECEIVED\",\n        \"status\": \"pre_transit\",\n        \"datetime\": \"2014-11-21T14:24:00Z\",\n        \"tracking_location\": { \"city\": null, \"state\": null, \"country\": null }\n      },\n      \"...\",\n      {\n        \"object\": \"TrackingDetail\",\n        \"message\": \"DELIVERED\",\n        \"status\": \"delivered\",\n        \"datetime\": \"2014-11-19T10:51:54Z\",\n        \"tracking_location\": { \"city\": \"SAN FRANCISCO\", \"state\": \"CA\", \"country\": \"US\" }\n      }\n    ]\n  }\n}\n```\n\n\n## Step 3: Engage With Your Customers\n\nOnce you're receiving webhooks, there's a lot of ways for you to re-engage with your customers. Here's a few ideas that some of our customers use:\n\nProvide a link to our public tracking page where your customers can check on their delivery progress at their leisure. All Vanlo users can add their branding to our tracking pages for a more customized feel. You can customize your tracking pages <a href=\"http://dashboard.vanlo.com/tracking-page\">here</a>. Just send them the tracking URL found on the public_url field of a <a href=\"/#trackers\">Tracker Object</a>.\n\nUse providers like <a href=\"https://www.twilio.com/\">Twilio</a> to send text message updates as their packages move through the mailstream."
    },
    {
      "id": "webhooks",
      "title": "Webhooks Guide",
      "content": "# Webhooks Guide\n\nThis guide will show you how to receive webhooks/event notifications for various Vanlo services.\n\nSeveral types of objects are processed asynchronously in the Vanlo system (<a href=\"/#batch-object\">Batches</a>, <a href=\"/#tracker-object\">Trackers</a>, and others). In order to update users with the status of these background tasks, Vanlo dispatches a webhook <a href=\"/#event-object\">Event</a> whenever a new event occurs. <a href=\"/#webhook-object\">Webhooks</a> are push notifications, or callbacks, which allow users to stay up-to-date on the status of their Vanlo objects without needing to poll for updates. Whenever a webhook is triggered, an Event is sent via HTTP POST to each configured webhook URL. Each of these webhooks expect a successful response; in the case of a failure, the Vanlo system will attempt to retry the webhook. After 8 retries over the course of a few days, Vanlo will disable the webhook.\n\nIn order to take advantage of webhooks, all you need to do is <a href=\"https://dashboard.vanlo.com/webhooks\">add your webhook URLs to your account page</a>. The way these webhook Events are processed will be specific to your application, but below is an example of a request's JSON body and a simple Sinatra application that removes problematic shipments from a Batch.\n\nA great way to learn more about the contents of each webhook Event is by using an endpoint mocking service such as Beeceptor. This would allow you to create a simple endpoint that you can configure to collect webhook requests made by Vanlo and inspect their contents. We don't use Beeceptor internally so please do research before sending any actual user data to them.\n\n<div class=\"guides-related-cards\">\n  <a class=\"guides-related-card\" href=\"#event-object\">\n    <div class=\"guides-related-card__text\">\n      <span class=\"guides-related-card__label\">RELATED LINK</span>\n      <span class=\"guides-related-card__title\">Event Object</span>\n    </div>\n    <svg class=\"guides-related-card__icon\" width=\"17\" height=\"17\" viewBox=\"0 0 17 17\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\"><path d=\"M8.25 11.25L11.25 8.25M11.25 8.25L8.25 5.25M11.25 8.25H5.25M15.75 8.25C15.75 12.3921 12.3921 15.75 8.25 15.75C4.10786 15.75 0.75 12.3921 0.75 8.25C0.75 4.10786 4.10786 0.75 8.25 0.75C12.3921 0.75 15.75 4.10786 15.75 8.25Z\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/></svg>\n  </a>\n  <a class=\"guides-related-card\" href=\"#introduction\">\n    <div class=\"guides-related-card__text\">\n      <span class=\"guides-related-card__label\">RELATED LINK</span>\n      <span class=\"guides-related-card__title\">API Documentation</span>\n    </div>\n    <svg class=\"guides-related-card__icon\" width=\"17\" height=\"17\" viewBox=\"0 0 17 17\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\"><path d=\"M8.25 11.25L11.25 8.25M11.25 8.25L8.25 5.25M11.25 8.25H5.25M15.75 8.25C15.75 12.3921 12.3921 15.75 8.25 15.75C4.10786 15.75 0.75 12.3921 0.75 8.25C0.75 4.10786 4.10786 0.75 8.25 0.75C12.3921 0.75 15.75 4.10786 15.75 8.25Z\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/></svg>\n  </a>\n</div>\n\n\n## Supported Events\n\nVanlo sends the following v1 webhook events, grouped by the object they describe. Each links to its payload reference. The full `result` payload for every event is documented in the API reference under <a href=\"#webhook-event-result\">Webhook Event Result</a>.\n\n**Batch**\n\n* [batch.created](#batch-created) - a new batch is created\n* [batch.updated](#batch-updated) - a batch changes (shipments added or removed, postage purchased, label generated)\n* [batch.completed](#batch-completed) - all shipments in a batch finish processing\n\n**Tracker**\n\n* [tracker.updated](#tracker-updated) - a tracker receives new carrier information\n* [tracker.detail.created](#tracker-detail-created) - a new tracking detail (carrier scan) is added\n\n**Refund**\n\n* [refund.successful](#refund-successful) - a shipment refund is approved by the carrier\n\n**Insurance**\n\n* [insurance.purchased](#insurance-purchased) - insurance is purchased for a shipment\n\n**Payment**\n\n* [payment.created](#payment-created) - a payment (account recharge) is initiated\n* [payment.completed](#payment-completed) - a payment clears\n* [payment.failed](#payment-failed) - a payment fails\n\n**Report**\n\n* [report.new](#report-new) - a report is requested and starts generating\n* [report.available](#report-available) - a report is ready to download\n* [report.failed](#report-failed) - a report fails to generate\n\n**Shipment**\n\n* [shipment.invoice.created](#shipment-invoice-created) - a carrier issues a post-purchase adjustment\n\n**Scan Form**\n\n* [scan_form.updated](#scan_form-updated) - a scan form is created or its status changes\n\n\n## Webhook POST JSON Example\n\n```json\n{\n  \"id\": \"evt_...\",\n  \"object\": \"Event\",\n  \"mode\": \"production\",\n  \"description\": \"batch.created\",\n  \"previous_attributes\": { \"state\": \"purchasing\" },\n  \"pending_urls\": [\"example.com/vanlo-webhook\"],\n  \"completed_urls\": [],\n  \"result\": {\n    \"id\": \"batch_...\",\n    \"object\": \"Batch\",\n    \"state\": \"purchased\",\n    \"num_shipments\": 1,\n    \"shipments\": [\n      {\n        \"batch_status\": \"postage_purchased\",\n        \"batch_message\": null,\n        \"id\": \"shp_a5b1348307694736aaqqqq8fqda53f93\"\n      }\n    ],\n    \"status\": { \"created\": 0, \"postage_purchased\": 1, \"postage_purchase_failed\": 0 },\n    \"label_url\": null\n  }\n}\n```\n\n\n## Retrieve a Webhook Event JSON Example\n\n```json\n{\n  \"description\": \"tracker.updated\",\n  \"mode\": \"test\",\n  \"previous_attributes\": {\n    \"status\": \"pre_transit\"\n  },\n  \"created_at\": \"2022-10-26T20:18:21.000Z\",\n  \"pending_urls\": [],\n  \"completed_urls\": [],\n  \"updated_at\": \"2022-10-26T20:18:21.000Z\",\n  \"id\": \"evt_55a53eb2556b11ed8945059f515d2b6d\",\n  \"user_id\": \"user_060ab38db3c04ffaa60f262e5781a9be\",\n  \"status\": \"pending\",\n  \"object\": \"Event\"\n}\n```\n\n\n## Receiving a Webhook Example\n\n```ruby\nrequire 'vanlo'\nrequire 'sinatra'\n\npost '/vanlo-webhook' do\n  result = params['result']\n\n  case result['object']\n  when 'Batch'\n    batch = Vanlo::Batch.new(result)\n\n    case batch.state\n    when 'purchase_failed'\n      batch.shipments.each do |shipment|\n        if shipment.batch_status == 'postage_purchase_failed'\n          batch.remove_shipments([shipment])\n        end\n      end\n    end\n  end\nend\n```\n\n```python\nimport vanlo\nfrom flask import Flask, request\n\napp = Flask(__name__)\nvanlo.api_key = 'VANLO_API_KEY'\n\n@app.route('/vanlo-webhook', methods=['POST'])\ndef vanlo_webhook():\n    event = request.get_json()\n    result = event['result']\n\n    if result['object'] == 'Batch':\n        batch = vanlo.Batch(**result)\n\n        if batch.state == 'purchase_failed':\n            failed = [\n                { \"id\": s['id'] }\n                for s in batch.shipments\n                if s['batch_status'] == 'postage_purchase_failed'\n            ]\n            if failed:\n                batch.remove_shipments(shipments=failed)\n\n    return '', 200\n```\n\n```php\n<?php\nrequire_once('/path/to/lib/vanlo.php');\n\\Vanlo\\Vanlo::setApiKey('VANLO_API_KEY');\n\n$payload = json_decode(file_get_contents('php://input'), true);\n$result  = $payload['result'];\n\nif ($result['object'] === 'Batch') {\n    $batch = \\Vanlo\\Batch::retrieve($result['id']);\n\n    if ($batch->state === 'purchase_failed') {\n        $failed = array();\n        foreach ($batch->shipments as $shipment) {\n            if ($shipment->batch_status === 'postage_purchase_failed') {\n                $failed[] = array('id' => $shipment->id);\n            }\n        }\n        if (!empty($failed)) {\n            $batch->remove_shipments(array('shipments' => $failed));\n        }\n    }\n}\n\nhttp_response_code(200);\n```\n\n```csharp\nusing Vanlo;\nusing Microsoft.AspNetCore.Mvc;\n\n[ApiController]\n[Route(\"vanlo-webhook\")]\npublic class VanloWebhookController : ControllerBase\n{\n    [HttpPost]\n    public IActionResult Post([FromBody] Event evt)\n    {\n        ClientManager.SetCurrent(\"VANLO_API_KEY\");\n\n        if (evt.result is Batch batch && batch.state == \"purchase_failed\")\n        {\n            var failed = new List<Dictionary<string, object>>();\n            foreach (var shipment in batch.shipments)\n            {\n                if (shipment.batch_status == \"postage_purchase_failed\")\n                {\n                    failed.Add(new Dictionary<string, object>() {\n                        { \"id\", shipment.id }\n                    });\n                }\n            }\n\n            if (failed.Count > 0)\n            {\n                batch.RemoveShipments(new Dictionary<string, object>() {\n                    { \"shipments\", failed }\n                });\n            }\n        }\n\n        return Ok();\n    }\n}\n```\n\n\n## Webhook Authentication\n\nOur recommended best practice for securing Webhooks involves either HMAC validation which has first-class support in each of our client libraries or using basic authentication and HTTPS on your endpoint. This will help prevent any altering of any information communicated to you by Vanlo, prevent any third parties from seeing your webhooks in transit, and will prevent any third parties from masquerading as Vanlo and sending fraudulent data. Vanlo performs certificate validation and requires any TLS-enabled (HTTPS) webhook recipients to have a certificate signed by a public trusted certification authority. We do not support sending webhooks over SSLv2, SSLv3, or any connection using so-called export-grade ciphers. For documentation on how to set up your server with TLS, we recommend <a href=\"https://wiki.mozilla.org/Security/Server_Side_TLS\">Mozilla's guide to Server-Side TLS</a> and <a href=\"https://www.ssllabs.com/projects/best-practices/\">Qualys's SSL/TLS deployment best practices guide</a>.\n\n\n## HMAC Validation\n\nSecuring a webhook via HMAC validation is simple. Pass a webhook_secret with your request to create or update a webhook as shown example below. Once a webhook secret is setup, we will return its signature via the X-Hmac-Signature header on every event sent to your webhook URL. All that's left is to validate that the signature we sent you matches the webhook secret you initially sent us. You can accomplish this by calling the validate_webhook() function if using one of our client libraries (the naming convention of this function may differ per language), and passing in your webhook secret, headers, and the event body. If the signatures match, the function will return the webhook data, otherwise it will throw an error to protect your system from the incoming webhook.\n\n\n## Basic Authentication\n\nBasic authorization requires that a username and password combination along with the webhook URL be passed during webhook creation. An example may look like this:https://username:secret@www.example.com/vanlo-webhook. When an event triggers in our system, we'll deliver a webhook to this endpoint along with an Authorization header that you will need to validate the credentials for. If the credentials do not match what we have stored in our system for this webhook (the basic auth header sent to this endpoint), the webhook should be rejected.\n\n\n### Create a Webhook\n\n```shell\ncurl -X POST https://www.vanlo.com/api/v1/webhooks \\\n  -H 'Authorization: Bearer VANLO_API_KEY' \\\n  -H 'Content-Type: application/json' \\\n  -d '{\n  \"webhook\": {\n    \"url\": \"https://example.com\",\n    \"webhook_secret\": \"A1B2C3\"\n  }\n}'\n```\n\n```ruby\nrequire 'vanlo'\nVanlo.api_key = 'VANLO_API_KEY'\n\nVanlo::Webhook.create(\n  url: 'https://example.com',\n  webhook_secret: 'A1B2C3'\n)\n```\n\n```python\nimport vanlo\nvanlo.api_key = 'VANLO_API_KEY'\n\nvanlo.Webhook.create(\n  url='https://example.com',\n  webhook_secret='A1B2C3'\n)\n```\n\n```php\nrequire_once('/path/to/lib/vanlo.php');\n\\Vanlo\\Vanlo::setApiKey('VANLO_API_KEY');\n\n$webhook = \\Vanlo\\Webhook::create(array(\n  'url'            => 'https://example.com',\n  'webhook_secret' => 'A1B2C3'\n));\n```\n\n```csharp\nusing Vanlo;\nClientManager.SetCurrent(\"VANLO_API_KEY\");\n\nWebhook webhook = Webhook.Create(\n    new Dictionary<string, object>() {\n        { \"url\",            \"https://example.com\" },\n        { \"webhook_secret\", \"A1B2C3\" }\n    }\n);\n```\n\n\n### Webhook JSON Response\n\n```json\n{\n  \"id\": \"hook_...\",\n  \"object\": \"Webhook\",\n  \"mode\": \"production\",\n  \"url\": \"http://example.com\",\n  \"disabled_at\": null\n}\n```\n\n\n## Frequently Asked Webhooks Questions:\n\n<div class=\"guides-faq\">\n  <div class=\"guides-faq__item\">\n    <p class=\"guides-faq__q\">How many times will Vanlo attempt to deliver the webhook Event to my URL endpoint?</p>\n    <p class=\"guides-faq__a\">After 8 failures, Vanlo will no longer attempt to send the webhook. There is an increasing delay between retries.</p>\n  </div>\n  <div class=\"guides-faq__item\">\n    <p class=\"guides-faq__q\">What HTTP status code do I need to return?</p>\n    <p class=\"guides-faq__a\">You should return a status code of 2XX. A 200 is preferred.</p>\n  </div>\n  <div class=\"guides-faq__item\">\n    <p class=\"guides-faq__q\">How long do I have to respond?</p>\n    <p class=\"guides-faq__a\">You must respond within 7 seconds. If no response is sent back, the webhook Event will be considered a failure and it will be sent again. It is a best practice to receive the webhook and send the Event to be processed by a background worker; this allows you to immediately return a successful response so you do not receive the webhook a second time.</p>\n  </div>\n  <div class=\"guides-faq__item\">\n    <p class=\"guides-faq__q\">How can I test my webhook integration?</p>\n    <p class=\"guides-faq__a\">All asynchronous actions that trigger webhooks in Production will also trigger webhooks in Test. For example, you could create a Test Tracker or purchase a Test Shipment. If you are developing locally and need a public URL for your webhooks, you can set one up at beeceptor or a similar endpoint mocking service.</p>\n  </div>\n  <div class=\"guides-faq__item\">\n    <p class=\"guides-faq__q\">Can I receive webhooks for packages not shipped through Vanlo?</p>\n    <p class=\"guides-faq__a\">In order to receive webhooks for packages not shipped through Vanlo, all you need to do is create a Vanlo Tracker object with the desired tracking code. Tracker objects send webhooks whenever new tracking Events are detected.</p>\n  </div>\n  <div class=\"guides-faq__item\">\n    <p class=\"guides-faq__q\">How can I retry failed Events?</p>\n    <p class=\"guides-faq__a\">We'll automatically retry failed attempts on your webhook, but currently there is not a way to retry failed events manually.</p>\n  </div>\n  <div class=\"guides-faq__item\">\n    <p class=\"guides-faq__q\">How many webhook endpoints can I designate?</p>\n    <p class=\"guides-faq__a\">Most users use 1-5, but you can setup up to 30 webhook endpoints. If you need more, just let us know.</p>\n  </div>\n</div>"
    }
  ]
}
