Download OpenAPI specification:Download
Swagger UI - Interactive API documentation using Swagger UI.
The Offers API is crafted for dealers and partners seeking to enhance their leasing operations for bicycles and accessories. By integrating this API, you can seamlessly align your workflows, minimize manual effort, reduce errors, and expedite the process through efficient status information updates. The API offers a comprehensive suite of functionalities:
These capabilities are designed to optimize the leasing process and enhance business operations through seamless API integration. This overview provides foundational insights into constructing offers using key components, thus ensuring seamless integration into your leasing workflow. The following section of this document will provide a detailed description of each component and how to leverage them effectively.
We are committed to continuously improving everything related to offers and refining the processes our partners utilize. Your feedback is invaluable to us, and we welcome you to get in touch if you're interested in partnering or have suggestions to make our offerings even better.
In the Offers API, the main offer object comprises several key components integral to creating a complete and leasable offer. Here’s a brief summary of its main parts:
This overview provides a foundational understanding of how to construct an offer using the essential components, ensuring seamless integration into the leasing workflow.
Offers submitted via this API are processed asynchronously, ensuring efficient background handling. Initially, upon submission, the service accepts the offer, returning an HTTP status code 202 Accepted. It's crucial to note that this does not confirm successful processing; subsequent background processes may identify issues such as missing or invalid data.
This section details potential statuses within the offer lifecycle and relevant developer actions:
pending: Awaiting acceptance by JobRad, indicating the initial submission state. [Initial status]
draft: The offer is accepted, yet to undergo further processing by JobRad.
order_journey_started: The offer has been integrated into an order, with the process now active. At this stage, no modifications are allowed.
employer_decision_pending: Awaiting employer approval, requiring developers to monitor and manage response setups.
jobrad_decision_pending: Awaiting JobRad's approval, similar to employer approval considerations.
done: The offer is complete, requiring no further actions. [Final status]
canceled: The offer is canceled; no subsequent processing occurs. [Final status]
rejected: The offer is deemed invalid and rejected. Ensure to check the errors field for rejection reasons. Correct the issues, then resubmit for processing.
The following is a typical response structure containing the "status" object. The "status" object provides detailed insight into the lifecycle and processing of offers and is returned in both the POST endpoint when submitting an offer and in all GET endpoints within the "data" object. This allows for tracking the current status of each offer.
{
"status": {
"current": {
"code": "pending",
"message": "The offer is currently processed by Jobrad.",
"translations": {
"de": "Das Angebot wird aktuell von JobRad bearbeitet."
}
},
"nextSteps": [
{
"code": "draft",
"message": "The offer is waiting for customer approval.",
"translations": {
"de": "Das Angebot wartet auf Zustimmung durch den Arbeitgeber."
}
},
{
"code": "canceled",
"message": "The offer is cancelled.",
"translations": {
"de": "Das Angebot wurde storniert."
}
},
{
"code": "rejected",
"message": "The offer has been rejected by Jobrad.",
"translations": {
"de": "Das Angebot wurde von Jobrad abgelehnt."
}
}
],
"errors": [
{
"code": "INTERNAL_SERVER_ERROR",
"message": "An internal error occurred. Please try again later.",
"translations": {
"de": "Es ist ein interner Fehler aufgetreten. Versuchen Sie es zu einem späteren Zeitpunkt erneut."
}
}
],
"infos": [
{
"code": "EXTERNAL_ID_ALREADY_USED",
"field": "externalOfferId",
"message": "The external ID XYZ is already associated with other offers.",
"translations": {
"de": "Die externalId XYZ ist bereits mit anderen Angeboten verknüpft."
}
}
]
}
}
current: Represents the current status of the offer, providing immediate information on where the offer stands in the processing lifecycle.
nextSteps: Lists potential status transitions and actions that could be taken next. This helps in forecasting the workflow progression and preparing for upcoming stages.
errors: Indicates issues that require action. These errors are critical and signify problems that must be addressed to proceed with the offer processing.
infos: Comprises informational messages that highlight changes or updates, such as normalization of text. These should be presented to the user but generally do not necessitate immediate action.
By understanding these elements, developers can efficiently manage offers, anticipate necessary actions, and ensure both accuracy and compliance in offer handling.
The diagram below illustrates the various statuses an offer can transition through during its lifecycle.
The externalOfferId is a identifier assigned by the dealer for an offer. It needs to be unique for a dealer.
Typically, it corresponds to the dealer's offer number.
Bidex Codes are integral to identifying bicycles and accessories within this API, facilitating streamlined classification and data exchange. These standardized six-digit codes promote efficient communication between suppliers and retailers.
Consider the Bidex Code 103030:
10 – Product category: Bicycles30 – Main group: City/Urban30 – Sub group: TouringThus, 103030 signifies a City Touring Bike.
For comprehensive details, refer to the Bidex website.
To streamline categorization, some Bidex Codes undergo internal remapping to a higher-level main group. For instance, codes such as 202120, 202130, 202140, and 202150 are remapped to 202100, as they represent gear system upgrades in JobRad's context.
When submitting an offer with a code like 202120, expect the offer retrieval process to return the remapped code 202100.
Use the GET /offers/accessory endpoint to:
This systematic approach to using Bidex Codes enhances data consistency and facilitates seamless integration into the leasing process.
All interactions with the Offers API require authentication using the OAuth 2.0 protocol.
To securely access protected endpoints, you must first obtain a valid access token from our authorization server. The process for retrieving and using these tokens is outlined in detail in our OAuth Authentication Guide.
Following these guidelines ensures that all requests are properly authenticated and authorized — providing secure, reliable access to the JobRad API.
Retrieve a specific offer by its UUID.
| id required | string <uuid> Example: a0ab7953-ca09-4bc6-8fee-5b003fccbb6d UUID of the offer to retrieve |
{- "data": {
- "id": "978fe89a-da63-4221-a8be-bbb743ef07bc",
- "externalOfferId": "external-id-123",
- "offerNumber": "FHA-000123",
- "orderNumber": "LAU-000105",
- "customerName": "Max Mustermann",
- "customerEmail": "max.mustermann@example.com",
- "product": {
- "bidexCode": 123040,
- "brandName": "Gudereit",
- "modelName": "Model X",
- "modelYear": 2023,
- "color": "Black",
- "size": "L",
- "driveManufacturer": "Bosch",
- "batteryCapacity": 500,
- "priceGross": 2900.99,
- "listPriceGross": 2999.99
}, - "accessory": [
- {
- "bidexCode": 301520,
- "brandName": "ABUS",
- "modelName": "Bordo Granit XPlus 6500",
- "priceGross": 99.99,
- "listPriceGross": 119.99
}
], - "withShipping": true,
- "shippingCostGross": 49.99,
- "installationCostGross": 29.99,
- "deliveryDate": "2030-12-01T00:00:00.000Z",
- "notes": "Please call before delivery",
- "createdAt": "2030-12-01T00:00:00.000Z",
- "lastUpdateAt": "2030-12-01T00:00:00.000Z",
- "status": {
- "current": {
- "code": "pending",
- "message": "The offer is awaiting acceptance by JobRad",
- "translations": {
- "de": "JobRad prüft gerade die Angaben Ihres Angebots."
}
}, - "nextSteps": [
- {
- "code": "draft",
- "message": "The offer is accepted, but not yet processed by JobRad.",
- "translations": {
- "de": "Der bzw. die Mitarbeitende hat jetzt Ihr Angebot erhalten."
}
}
], - "errors": [ ],
- "infos": [
- {
- "code": "info",
- "message": "Bike brand name 'CUBE' mapped to 'Cube'",
- "field": "product.brandName",
- "translations": {
- "de": "Bike-Brandname 'CUBE' wurde auf 'Cube' umgemappt."
}
}
]
}, - "links": {
}
}, - "meta": {
- "timestamp": "2024-12-04T14:05:56.222858Z"
}
} Update an existing offer by its UUID.
- The processing of this operation is asynchronous. For more information on asynchronous processing, see the
*General Notes -> Asynchronous Processing* section above.
- It is possible that updates cannot be applied if previous changes have not yet been processed. In this case,
the offer will be rejected with a 406 status code and error code *OFFER_IS_BEING_PROCESSED*.
| id required | string <uuid> |
required | object (Product) Represents the product information |
required | Array of objects (Accessory) List of accessories for the bicycle |
| withShipping | boolean Shipping is part of the offer |
| shippingCostGross | number <double> Gross costs for shipping |
| installationCostGross | number <double> Gross costs for installation |
| deliveryDate | string <date-time> Expected delivery date |
| notes | string Additional notes for the offer |
{- "product": {
- "bidexCode": 123040,
- "brandName": "Gudereit",
- "modelName": "Model X",
- "modelYear": 2023,
- "color": "Black",
- "size": "L",
- "driveManufacturer": "Bosch",
- "batteryCapacity": 500,
- "priceGross": 2900.99,
- "listPriceGross": 2999.99
}, - "accessory": [
- {
- "bidexCode": 301520,
- "brandName": "ABUS",
- "modelName": "Bordo Granit XPlus 6500",
- "priceGross": 99.99,
- "listPriceGross": 119.99
}
], - "withShipping": true,
- "shippingCostGross": 49.99,
- "installationCostGross": 29.99,
- "deliveryDate": "2030-12-01T00:00:00.000Z",
- "notes": "Please call before delivery"
}{- "errors": [
- {
- "code": "MISSING_PARAMETER",
- "message": "Attribute must be specified: Offer.customerName",
- "field": "Offer.customerName",
- "translations": {
- "de": "Das folgende Attribute muss angegeben werden: Offer.customerName"
}
}, - {
- "code": "INVALID_PARAMETER",
- "message": "Attribute must be a valid email address: invalid-mail-example.com",
- "field": "Offer.customerEmail",
- "translations": {
- "de": "Das Feld muss eine gültige Email-Adresse sein: invalid-mail-example.com"
}
}, - {
- "code": "INVALID_PARAMETER",
- "message": "Attribute 'Accessory.priceGross' must be less than the 'Accessory.listPriceGross'",
- "field": "Accessory.priceGross",
- "translations": {
- "de": "Der Zubehör Brutto-Preis muss geringer als die UVP des Zubehörs sein: 199.99 > 119.99"
}
}
]
}Cancel a specific offer by its UUID
| id required | string <uuid> Example: a0ab7953-ca09-4bc6-8fee-5b003fccbb6d UUID of the offer to cancel |
| contactFromEmployeeRequested | boolean |
| cancelReason | string Example: cancelReason=customer_wants_different_bicycle Reason for canceling the offer. Valid values: customer_wants_different_bicycle, not_deliverable, delivery_date_cannot_be_met, manufacturer_recall, other |
{- "errors": [
- {
- "code": "UNAUTHORIZED",
- "message": "You are not authorized to access this resource.",
- "translations": {
- "de": "Sie haben keine Berechtigung für die angeforderte Ressource"
}
}
]
}Get all offers belonging to the current dealer that match the specified filter criteria
| creationDateFrom | string <date-time> Example: creationDateFrom=2023-10-01T00:00:00.000Z The starting datetime (ISO-8601 format) for filtering offers by creation date |
| creationDateTo | string <date-time> Example: creationDateTo=2023-10-31T23:59:59.999Z The ending datetime (ISO-8601 format) for filtering offers by creation date |
| statusChangedDateFrom | string <date-time> Example: statusChangedDateFrom=2023-10-01T00:00:00.000Z The starting datetime (ISO-8601 format) for filtering offers by the date their status was last changed. |
| statusChangedDateTo | string <date-time> Example: statusChangedDateTo=2023-10-01T00:00:00.000Z The ending datetime (ISO-8601 format) for filtering offers by the date their status was last changed. |
| status | string Example: status=pending,!rejected
|
| externalOfferId | string Example: externalOfferId=EX01,!EX02 Multiple comma separated values allow, the filter can be negated with ! |
string Example: email=j.dow@gmail.com,!j.dow@jobrad.com Multiple comma separated values allow, the filter can be negated with ! | |
| offerNumber | string Example: offerNumber=FHA-190,!FHA-200 Multiple comma separated values allow, the filter can be negated with ! |
| search | string Example: search=Mi
|
| page | integer <int32> Default: 1 Example: page=1 Page number for pagination (starts from 1) |
| perPage | integer <int32> Default: 10 Example: perPage=10 Number of items per page |
| sortBy | string Default: "createdAt" Enum: "createdAt" "status" "externalOfferId" "customerEmail" "customerName" "totalPriceGross" "offerNumber" "productName" Example: sortBy=createdAt Field to sort by. Available options: createdAt, status, externalOfferId, customerEmail, customerName, totalPriceGross, offerNumber, productName. Note: productName sorts by brand name first, then by model name (two-tier sort). |
| order | string Default: "desc" Enum: "asc" "desc" Example: order=desc Sort order: asc (ascending) or desc (descending) |
{- "data": [
- {
- "id": "978fe89a-da63-4221-a8be-bbb743ef07bc",
- "externalOfferId": "external-id-123",
- "offerNumber": "FHA-000123",
- "orderNumber": "LAU-000105",
- "customerName": "Max Mustermann",
- "customerEmail": "max.mustermann@example.com",
- "product": {
- "bidexCode": 123040,
- "brandName": "Gudereit",
- "modelName": "Model X",
- "modelYear": 2023,
- "color": "Black",
- "size": "L",
- "driveManufacturer": "Bosch",
- "batteryCapacity": 500,
- "priceGross": 2900.99,
- "listPriceGross": 2999.99
}, - "accessory": [
- {
- "bidexCode": 301520,
- "brandName": "ABUS",
- "modelName": "Bordo Granit XPlus 6500",
- "priceGross": 99.99,
- "listPriceGross": 119.99
}
], - "withShipping": true,
- "shippingCostGross": 49.99,
- "installationCostGross": 29.99,
- "deliveryDate": "2030-12-01T00:00:00.000Z",
- "notes": "Please call before delivery",
- "createdAt": "2030-12-01T00:00:00.000Z",
- "lastUpdateAt": "2030-12-01T00:00:00.000Z",
- "status": {
- "current": {
- "code": "pending",
- "message": "The offer is awaiting acceptance by JobRad",
- "translations": {
- "de": "JobRad prüft gerade die Angaben Ihres Angebots."
}
}, - "nextSteps": [
- {
- "code": "draft",
- "message": "The offer is accepted, but not yet processed by JobRad.",
- "translations": {
- "de": "Der bzw. die Mitarbeitende hat jetzt Ihr Angebot erhalten."
}
}
], - "errors": [ ],
- "infos": [
- {
- "code": "info",
- "message": "Bike brand name 'CUBE' mapped to 'Cube'",
- "field": "product.brandName",
- "translations": {
- "de": "Bike-Brandname 'CUBE' wurde auf 'Cube' umgemappt."
}
}
]
}, - "links": {
}
}
], - "meta": {
- "totalCount": 1,
- "page": 1,
- "perPage": 10,
- "sortBy": "createdAt",
- "order": "desc"
}
} Submit a new offer with product and accessory information.
- The processing of this operation is asynchronous. For more information on asynchronous processing, see the
*General Notes -> Asynchronous Processing* section above.
- The offer will be created in an initial status *pending*.
| externalOfferId required | string External ID for the offer |
| customerName required | string Full name of the customer |
| customerEmail required | string^[a-zA-Z0-9_+&*-]+(?:\.[a-zA-Z0-9_+&*-]+)*@(?... Customer's email address for communication |
required | object (Product) Represents the product information |
required | Array of objects (Accessory) List of accessories for the bicycle |
| withShipping | boolean Shipping is part of the offer |
| shippingCostGross | number <double> Gross costs for shipping |
| installationCostGross | number <double> Gross costs for installation |
| deliveryDate | string <date-time> Expected delivery date |
| notes | string Optional notes for the offer |
{- "externalOfferId": "external-id-123",
- "customerName": "Max Mustermann",
- "customerEmail": "max.mustermann@example.com",
- "product": {
- "bidexCode": 123040,
- "brandName": "Gudereit",
- "modelName": "Model X",
- "modelYear": 2023,
- "color": "Black",
- "size": "L",
- "driveManufacturer": "Bosch",
- "batteryCapacity": 500,
- "priceGross": 2900.99,
- "listPriceGross": 2999.99
}, - "accessory": [
- {
- "bidexCode": 301520,
- "brandName": "ABUS",
- "modelName": "Bordo Granit XPlus 6500",
- "priceGross": 99.99,
- "listPriceGross": 119.99
}
], - "withShipping": true,
- "shippingCostGross": 49.99,
- "installationCostGross": 29.99,
- "deliveryDate": "2030-12-01T00:00:00.000Z",
- "notes": "Please call before delivery"
}{- "data": {
- "id": "a0ab7953-ca09-4bc6-8fee-5b003fccbb6d",
- "status": "pending"
}, - "meta": {
- "timestamp": "2024-12-04T14:05:56.222858Z"
}
}The offer history endpoint is used to retrieve the history of a specific offer based on its UUID. From a business perspective, this means that all changes made to an offer, along with relevant metadata (e.g., who made the change and when), are returned. This allows the full lifecycle of an offer to be tracked and understood.
| id required | string <uuid> |
| page | integer <int32> Default: 1 |
| perPage | integer <int32> Default: 10 |
{- "errors": [
- {
- "code": "UNAUTHORIZED",
- "message": "You are not authorized to access this resource.",
- "translations": {
- "de": "Sie haben keine Berechtigung für die angeforderte Ressource"
}
}
]
} Retrieve all leasable bike and accessory entries with BIDEX code and category path:
- BIDEX Codes: Used to identify bicycles and accessories. More information about BIDEX codes can be found above
under the section "General Notes -> BIDEX Codes"'.
- JobradExtension: Marks JobRad-specific BIDEX categories that are not part of the standard.
string Example: email=gabi.gabel@jobrad.org Email of an employee. This labels the leasable product categories to only the ones allowed for this employee if it is a registered employee. | |
| lang | string Example: lang=de The language of the category names that should be returned. If the language is not available, German will be provided. |
{- "data": {
- "bikeCategories": [
- {
- "bidexCode": "10",
- "name": "Fahrrad",
- "jobradCategoryDe": "Fahrrad",
- "jobradExtension": false,
- "leasable": true,
- "maxQuantity": 1,
- "maxPriceSales": 0,
- "maxPriceRrp": 0,
- "minPriceRrp": 0,
- "subGroups": [
- {
- "bidexCode": "1010",
- "name": "MTB",
- "jobradCategoryDe": "MTB - Cross",
- "jobradExtension": false,
- "leasable": true,
- "maxQuantity": 1,
- "maxPriceSales": 0,
- "maxPriceRrp": 0,
- "minPriceRrp": 0,
- "subGroups": [
- {
- "bidexCode": "101010",
- "name": "Hardtail",
- "jobradCategoryDe": "Hardtail",
- "jobradExtension": false,
- "leasable": true,
- "maxQuantity": 1,
- "maxPriceSales": 0,
- "maxPriceRrp": 0,
- "minPriceRrp": 0,
- "subGroups": [ ]
}
]
}
]
}
], - "accessoryCategories": [
- {
- "bidexCode": "30",
- "name": "Zubehör",
- "jobradCategoryDe": "Zubehör",
- "jobradExtension": false,
- "leasable": true,
- "maxQuantity": 1,
- "maxPriceSales": 0,
- "maxPriceRrp": 0,
- "minPriceRrp": 0,
- "subGroups": [
- {
- "bidexCode": "3010",
- "name": "Kindersitze",
- "jobradCategoryDe": "Kindertransport",
- "jobradExtension": false,
- "leasable": true,
- "maxQuantity": 1,
- "maxPriceSales": 0,
- "maxPriceRrp": 0,
- "minPriceRrp": 0,
- "subGroups": [
- {
- "bidexCode": "301010",
- "name": "Kindersitze hinten",
- "jobradCategoryDe": "Kindersitz",
- "jobradExtension": false,
- "leasable": true,
- "maxQuantity": 1,
- "maxPriceSales": 0,
- "maxPriceRrp": 0,
- "minPriceRrp": 0,
- "subGroups": [ ]
}
]
}
]
}
], - "filtered": false
}, - "meta": {
- "timestamp": "2024-12-04T14:05:56.222858Z"
}
} Retrieve all leasable accessory entries with Bidex code and category path:
- **Bidex Codes**: Used to identify bicycles and accessories. More information about Bidex codes can be found above
under the section *General Notes -> Bidex Codes*.
- **JobradExtension**: Marks JobRad-specific Bidex categories that are not part of the standard.
{- "data": [
- {
- "bidexCode": "204070",
- "jobradCategoryDe": "Alle / Zubehör / Sonstiges / Laufradsatz-Upgrade",
- "jobradExtension": true
}, - {
- "bidexCode": "301010",
- "jobradCategoryDe": "Alle / Zubehör / Kindertransport / Kindersitz",
- "jobradExtension": false
}
], - "meta": {
- "timestamp": "2024-12-04T14:05:56.222858Z"
}
}