Best practices

Learn how to efficiently consume BFG APIs.

This page outlines the recommended approach to consuming BFG APIs, ensuring clarity, reliability, and maintainability of client applications.

Using API design conventions

Response structures

Structure responses predictably. Each response should consistently use a root data key containing payload objects or arrays. Make sure to distinguish single-object responses from multiple-object responses.

For single resources

The data field contains an object.

{
  "data": {
    "id": 1,
    "title": "Smithson MAP Policy 2024",
    ...
  }
}

For collections

The data field contains an array of objects. Use the links and meta objects to paginate.

{
  "data": [ ... ],
  "links": { ... },
  "meta": { ... }
}

Naming conventions

Use snake_case consistently for fields:

  • Correct: map_policy_type_id
  • Incorrect: mapPolicyTypeID

Clearly named fields help developers intuitively understand the payload without frequent consultation of documentation.

Handling pagination

Use the standard meta and links fields to clearly convey pagination. For example, use links for navigation and meta to understand the pagination state:

"links": {
  "first": "...",
  "last": "...",
  "prev": null,
  "next": null
},
"meta": {
  "current_page": 1,
  "from": 1,
  "last_page": 1,
  "per_page": 10,
  "to": 7,
  "total": 7
}

Make sure navigation links (first, last, prev, next) correctly indicate available navigation options.

Note: Paginate using the page query parameter. Default page size is 10 unless otherwise documented.

Interpreting nullable and optional fields

Always expect fields such as contact_email, file_url, or contents to be nullable. If a value is not set, it will be explicitly returned as null.

"file_url": null

Avoid relying on missing fields—fields are never omitted.

Using feedback and error details

Use the message field to clearly communicate operational outcomes. For example, detail reasons for partial failures or skipped operations to enable straightforward debugging and corrective actions:

{
  "data": {
    "skipped_records": [
      {
        "upc": "082442935631",
        "reason": "Product not found."
      }
    ]
  },
  "message": "Product inventories updated successfully."
}

Some responses may include a message field and a skipped_records array. Use these for diagnostics during batch operations.

Handling date and time

Timestamps are in the ISO 8601 format: YYYY-MM-DDTHH:MM:SS.sssZ. So follow the ISO 8601 format for date and time consistency and clarity:

"created_at": "2024-10-11T23:04:05.000000Z"

Also, make sure to explicitly state the timezone (Z denotes UTC).

Some legacy fields, like created_at, may use a space delimiter (YYYY-MM-DD HH:MM:SS) so always parse accordingly.

Using boolean and status representations

Represent booleans explicitly with true or false. Avoid using strings for binary states:

"allow_add_to_cart": false

For states requiring more than two values, prefer clear enumerations:

"allow_international_shipments": "REQUIRES_APPROVAL"

Common status values for fields, like allow_free_shipping, might include the following:

  • YES
  • NO
  • REQUIRES_APPROVAL

Do not assume boolean values or numeric codes.

Specifying URLs and file links

Provide direct URLs clearly for downloadable resources:

"file_url": "https://bfg-atlas.s3.amazonaws.com/map.policies/files/1728773864-test.txt"

You should always check if the URL is null before attempting access.

Working with nested data

Many resources include embedded nested JSON objects. Parse these as part of the response structure:

"map_advertising_pricing": {
  "code": "MAP_MINIMUM",
  "description": "MAP is Minimum Price"
}

Using relationships and linked data

Fields such as company, retailer, or map_promotions often appear as nested objects. Always parse full relationships instead of relying solely on *_id fields:

"company": {
  "name": "Ratke Ltd",
  "account_id": "c4569d6c-0d9d-4a5b-9af6-13ec566d76a4"
}

Examples of a well-structured client request

GET request

Here's an example of a GET request for the List Products API:

curl --request GET \
  --url "https://api-sandbox.buyingfreedom.app/api/products\
?sort=title\
&page=1\
&page_size=10\
&include=company,map_promotions,inventories\
&include_inventory=true\
&filter[with_inventory]=true\
&filter[retailer_ids]=3,4\
&filter[inventory_sources]=store" \
  --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
  --header "Content-Type: application/json" \
  --header "Accept: application/json"

POST request

Here's an example of a POST request for the Register a Store API:

curl --request POST \
  --url "https://api-sandbox.buyingfreedom.app/api/store-locator/register" \
  --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
  --header "Content-Type: application/json" \
  --header "Accept: application/json" \
  --data '{
    "brand_account_id": "a7f8c122-3b9d-42cb-91d2-9f443fc94187",
    "ffl_license_number": "183405029G20417",
    "name": "Canyon Ridge Outfitters",
    "logo": "https://example-cdn.com/logos/canyon-ridge-logo.png",
    "phone": "3125550198",
    "email": "[email protected]",
    "website": "https://www.canyonridgegear.com",
    "store_pickup": true,
    "online_shopping": false,
    "address_1": "742 Meadowbrook Way",
    "address_2": "Suite B",
    "address_3": null,
    "city": "Fairmont",
    "state": "WI",
    "postal_code": "53716",
    "country": "US"
  }'

PUT request

Here's an example of a PUT request for the Bulk Update Product Inventories:

curl --request PUT \
  --url "https://api-sandbox.buyingfreedom.app/api/product-inventories" \
  --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
  --header "Content-Type: application/json" \
  --header "Accept: application/json" \
  --data '{
    "account_id": "d83a4e1f-4c0b-4e35-a2c5-22d2e962a1f4",
    "product_inventories": [
      {
        "upc": "012345678905",
        "quantity": 45
      },
      {
        "upc": "987654321000",
        "quantity": 120
      },
      {
        "upc": "082442935631",
        "quantity": 30
      }
    ]
  }'