--- components: schemas: contacts.contact.address.v1: properties: city: example: Lebanon country: description: |- Leave this field blank (absent, empty string, or null) if the contact is in the United States. For non-US addresses, use the English name of the country (e.g. Germany rather than Deutschland). Place other elements of the address in the `city`, `state`, and `postal_code` fields normally. For example, even though German address blocks include the postal code before the city name, keep the city in the `city` field and the postal code in the `postal_code` field. Our software automatically reformats international addresses as needed when printing envelopes. example: '' postal_code: example: '03766' state: example: NH street: description: The street address may contain multiple lines. example: 123 Main St. type: object contacts.contact.update.v1: properties: address: $ref: '#/components/schemas/contacts.contact.address.v1' company: description: The contact's company, church, or other organization. It will be printed beneath the name and above the street address. example: ~ external_id: description: Use this field to store your application's identifier for this contact. example: ~ file_as: description: |- The name we should use when showing this contact in a list. It is usually in the format `LastName, FirstName` for individuals, or just the regular name for companies and churches. If the `file_as` parameter is not included, one will be generated automatically, based on the contact's name. example: Tischler, Joe greeting: description: |- The name we should use in the first line of a mail-merged letter, usually following the word "Dear". If a 'greeting' isn't included, one will be generated automatically, based on the contact's name. example: Joe and Mary name: description: The contact's full name, as it should appear on the first line of an envelope. example: Joe and Mary Tischler type: object contacts.contact.v1: properties: address: $ref: '#/components/schemas/contacts.contact.address.v1' company: description: The contact's company, church, or other organization. It will be printed beneath the name and above the street address. example: ~ contact_id: example: c-###-#### type: string external_id: description: Use this field to store your application's identifier for this contact. example: ~ file_as: description: |- The name we should use when showing this contact in a list. It is usually in the format `LastName, FirstName` for individuals, or just the regular name for companies and churches. If the `file_as` parameter is not included, one will be generated automatically, based on the contact's name. example: Tischler, Joe greeting: description: |- The name we should use in the first line of a mail-merged letter, usually following the word "Dear". If a 'greeting' isn't included, one will be generated automatically, based on the contact's name. example: Joe and Mary name: description: The contact's full name, as it should appear on the first line of an envelope. example: Joe and Mary Tischler type: object contacts.contact_created.v1: properties: contact_id: example: c-###-#### type: string type: object contacts.contact_created.v2: properties: contact_id: example: c-###-#### type: string external_id: {} type: object contacts.contact_wrapper.v1: properties: contact: $ref: '#/components/schemas/contacts.contact.v1' description: A contact object. type: object contacts.contacts_created.v1: properties: contacts: items: $ref: '#/components/schemas/contacts.contact_created.v2' type: array type: object contacts.v1: properties: contacts: description: An array of contact objects. items: $ref: '#/components/schemas/contacts.contact.v1' type: array type: object contacts.webhook.address_update.v1: properties: code: description: |- The type of event that triggered this webhook. This code is intended for use by your app and should generally not be displayed to users. The following codes are currently in use: - `unspecified`: The address was updated without a reason being provided. The accompanying message will typically be blank, but may contain a message, and should be displayed to the end user if present. - `api_client`: The address was updated by an API client. - `returned_mail`: A letter was returned to us. It may include a forwarding address. In order to allow the list of codes to expand over time, your application MUST treat any unrecognized code as `unspecified`. example: returned_mail contact_id: description: The contact ID used by prayerletters.com example: c-###-#### type: string external_id: description: The contact ID supplied by your integration (or whichever integration created the contact). message: description: A single line of free-form text providing more detail about the event and new address. This is intended to be displayed to users to provide information about why the event was triggered. It is often a custom message typed by one of our employees and should not be parsed by your app. example: Letter Returned -- Undeliverable As Addressed name: description: The contact's full name, as it should appear on the first line of an envelope. example: Joe and Mary Tischler new_address: $ref: '#/components/schemas/contacts.webhook.address_update.v1.address' description: |- The updated address record. Use the `is_deliverable` field to determine whether or not the new address is valid. For example, if we receive a move-update notice saying that a PO Box was closed and no new address was provided, `is_deliverable` will be false. example: city: Lebanon country: '' is_deliverable: false postal_code: '03766' state: NH street: 123 Main St. old_address: $ref: '#/components/schemas/contacts.webhook.address_update.v1.address' description: |- The address record as it existed prior to the change. Note that the `is_deliverable` field does not indicate whether the old address was deliverable, but rather whether it was considered to be deliverable before the event that triggered this webhook. For example, if we receive a returned letter without a forwarding address, we will send an address update event with the same old and new address. In this case, `is_deliverable` will be true in `old_address` and false in `new_address`. example: city: Lebanon country: '' is_deliverable: true postal_code: '03766' state: NH street: 123 Main St. updated: description: When the event was triggered. You should check to ensure that the user hasn't changed the address in your app between when the event was created and when the webhook arrived at your app, in case of delayed messages. format: date-time type: string type: object contacts.webhook.address_update.v1.address: properties: city: example: Lebanon country: example: '' is_deliverable: description: Whether or not we consider the address to be valid for receiving mail. postal_code: example: '03766' state: example: NH street: example: 123 Main St. type: object files.file.v1: properties: download_href: example: /api/v1/files/f-###-####/download format: uri type: string filename: example: newsletter.pdf id: example: f-###-#### type: string type: object files.file_uploaded.v1: properties: filename: example: myfile.pdf id: example: f-###-#### type: string type: object hooks.hook.v1: properties: event: description: The name of webhook that this subscription follows. href: description: The URL of this webhook subscription. To delete or make changes to the webhook, you would make a DELETE or POST call to this URL. example: /api/v1/hooks/h-###-#### format: uri type: string id: example: h-###-#### type: string last_triggered: description: The last time this subscription received an event. This timestamp is set when the event is generated, not when the webhook target is successfully contacted. format: date-time type: string target: description: The URL to be notified when the requested event is triggered. It must be an HTTP or HTTPS address and may contain query parameters. example: https://example.com/api/hooks?token=abc123 format: uri type: string type: object hooks.ping_response.v1: properties: status: description: The HTTP status code returned by the webhook subscription's target upon receiving the ping event. example: '200' type: object hooks.v1: properties: events: additionalProperties: $ref: '#/components/schemas/hooks.v1.event' type: object subscriptions: items: $ref: '#/components/schemas/hooks.hook.v1' type: array type: object hooks.v1.event: properties: description: description: A brief description of the event that triggers the webhook. url: description: A web page describing the webhook. format: uri type: string type: object orders.resend_created.v1: properties: created: format: date-time type: string order_id: example: m-###-#### type: string type: object orders.resend_to_list.v1: properties: list_id: example: l-###-#### type: string saved_order_id: example: l-###-#### type: string type: object orders.saved_order.v1: properties: created: format: date-time type: string name: {} saved_order_id: example: l-###-#### type: string type: object orders.saved_orders.v1: properties: saved_orders: description: An array of saved order objects. items: $ref: '#/components/schemas/orders.saved_order.v1' type: array type: object orders.webhook.order_complete.v1: properties: completed: format: date-time type: string order_id: example: m-###-#### type: string type: object templates.webhook.shipped.v1.customer: properties: id: example: s-###-#### type: string name: example: Joe Carpenter type: object templates.webhook.shipped.v1.order: properties: created: format: date type: string id: example: m-###-#### type: string quantity: example: 100 shipped: format: date type: string type: object templates.webhook.shipped.v1.template: properties: code: example: event_postcard id: example: t-###-#### type: string name: example: Special Event Invitation Postcard type: object securitySchemes: access_token: description: |- An OAuth access token may be provided as part of the URL query. This is less secure than using a bearer token since the URL gets logged, but it's convenient for testing from a browser or command line. We recommend that this method only be used during development. in: query name: access_token type: apiKey bearer_token: description: This is the recommended way to provide an access token received via OAuth. If your HTTP library doesn't support the Bearer method, you may instead pass it as the username portion of the Basic method (leave the password blank). scheme: bearer type: http oauth: description: API access is provided using OAuth 2.0. If you don't yet have client credentials for accessing the API, contact us with your desired use case and we'll be happy to get you set up. flows: authorizationCode: authorizationUrl: https://www.prayerletters.com/my/oauth/authorize scopes: contacts.read: Access your mailing list contacts.write: Change your mailing list hooks.manage: Update and delete webhooks created by other applications orders.read: Access your order information orders.write: Create orders on your behalf pls.files.read: See files that have been uploaded to your account pls.files.write: Add files to your account pls.lists.read: Access your temporary mailing lists pls.lists.write: Create and update temporary mailing lists tokenUrl: https://www.prayerletters.com/oauth/token type: oauth2 info: title: Prayer Letter Service API version: '0.1' openapi: 3.1.0 paths: /v1/contacts: delete: description: This call is included for the sake of completeness, but it should almost never actually be used. If you upload a new set of contacts using PUT, any existing contacts will automatically be removed. responses: '204': description: Successful response security: - access_token: [] - bearer_token: [] - oauth: - contacts.write summary: Delete all contacts tags: - contacts get: description: Download the active mailing list. responses: '200': content: application/json: schema: $ref: '#/components/schemas/contacts.v1' description: Successful Response security: - access_token: [] - bearer_token: [] - oauth: - contacts.read summary: Get all contacts tags: - contacts put: description: |- Remove all current contacts and upload a new mailing list. The body of the request should be formatted identically to the response from the GET call. When you include a non-null `external_id` field with a record, the response will contain a mapping between your ID and the `contact_id` at prayerletters.com. You can then use the `contact_id` to update or delete the record as needed. requestBody: content: application/json: schema: $ref: '#/components/schemas/contacts.v1' responses: '200': content: application/json: schema: $ref: '#/components/schemas/contacts.contacts_created.v1' description: Successful Response security: - access_token: [] - bearer_token: [] - oauth: - contacts.write summary: Replace the mailing list tags: - contacts /v1/contacts/{id}: delete: description: Remove a contact from the database. parameters: - description: The ID of the contact to be deleted, as returned by GET. It typically follows the format `c-###-####`, but is not guaranteed to do so, and should be treated as an opaque string. in: path name: id required: true schema: example: c-###-#### type: string responses: '204': description: |- Successful response. We keep a record of deleted contacts, and will also return status 204 (No Content) if the contact previously existed but has already been deleted. security: - access_token: [] - bearer_token: [] - oauth: - contacts.write summary: Delete a contact tags: - contacts get: description: Retrieve a single mailing list contact. parameters: - description: The ID of the contact to be retrieved. It typically follows the format `c-###-####`, but is not guaranteed to do so, and should be treated as an opaque string. in: path name: id required: true schema: example: c-###-#### type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/contacts.contact.v1' description: Successful Response security: - access_token: [] - bearer_token: [] - oauth: - contacts.read summary: Get a contact tags: - contacts /v1/files: post: parameters: - example: myfile.pdf in: query name: filename required: true schema: type: string requestBody: content: '*/*': {} description: Include the contents of the file as the request body. The content type header will be ignored; we use the file extension to determine the file type. required: true responses: '201': content: application/json: schema: $ref: '#/components/schemas/files.file_uploaded.v1' description: Successful response security: - access_token: [] - bearer_token: [] - oauth: - files.write summary: Upload a file tags: - files /v1/files/{id}: delete: description: Delete a file that hasn't yet been associated with an order. parameters: - description: The ID of the file. It typically follows the format `f-###-####`, but is not guaranteed to do so. in: path name: id required: true schema: example: f-###-#### type: string responses: '204': description: This response code will also be used if the file doesn't exist or was previously deleted. security: - access_token: [] - bearer_token: [] - oauth: - files.write summary: Delete a file tags: - files get: description: Retrieve information about an uploaded file. parameters: - description: The ID of the file. It typically follows the format `f-###-####`, but is not guaranteed to do so. in: path name: id required: true schema: example: f-###-#### type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/files.file.v1' description: Successful Response security: - access_token: [] - bearer_token: [] - oauth: - files.read summary: Get File Metadata tags: - files /v1/files/{id}/download: get: parameters: - description: The ID of the file. It typically follows the format `f-###-####`, but is not guaranteed to do so. in: path name: id required: true schema: example: f-###-#### type: string responses: '200': description: The response body will contain the contents of the file. See the Content-Disposition header for the filename. security: - access_token: [] - bearer_token: [] - oauth: - files.read summary: Download a file tags: - files /v1/hooks: get: description: This will give you a list of events to which your API token can subscribe, as well as a list of existing subscriptions. If you have the `hooks.manage` scope, you will see all webhook subscriptions; otherwise, you'll only see subscriptions that you've created. responses: '200': content: application/json: schema: $ref: '#/components/schemas/hooks.v1' description: Successful Response summary: Get information about all webhooks and subscriptions. tags: - hooks post: description: |- Subscribe to receive notifications about an event. When a webhook subscription is created, we will immediately trigger an asynchronous ping event at the target URL, so that you can be sure the subscription is working. See the [Ping a webhook target][ping] API call for details. Unlike normal notifications, this event will not be retried if there's an error. [ping]: /dev/api/v1/hooks/:id/ping/GET parameters: - description: The name of the webhook event. in: query name: event schema: type: string - description: The URL that should be notified when the event occurs. The URL may contain query parameters, though a parameter will be overwritten if it has the same name as a query parameter provided by the webhook. in: query name: target schema: type: .uri responses: '201': content: application/json: schema: $ref: '#/components/schemas/hooks.hook.v1' description: Successful Response summary: Subscribe to receive notifications about an event. tags: - hooks /v1/hooks/{id}: delete: description: Delete a webhook subscription. parameters: - in: path name: id required: true schema: example: h-###-#### type: string responses: '204': description: Successful Response summary: Delete a webhook tags: - hooks get: description: Retrieve details about a webhook subscription. parameters: - in: path name: id required: true schema: example: h-###-#### type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/hooks.hook.v1' description: Successful Response summary: Get a webhook tags: - hooks post: description: |- Change the target URL for a webhook subscription. It isn't currently possible to change the event for a given webhook subscription. If you need to do this, create a new subscription and delete the old one. parameters: - in: path name: id required: true schema: example: h-###-#### type: string - description: The URL that should be notified when the event occurs. The URL may contain query parameters, though a parameter will be overwritten if it has the same name as a query parameter provided by the webhook. in: query name: target schema: type: .uri responses: '200': content: application/json: schema: $ref: '#/components/schemas/hooks.hook.v1' description: Successful Response summary: Change the target URL for a webhook subscription tags: - hooks /v1/hooks/{id}/ping: get: description: |- Triggers a webhook on demand, for testing purposes. You can call this URL from your browser or from a command line (don't forget to include your `access_token` in the URL as well), and we will immediately and synchronously send a `ping` event to the webhook subscription's target URL. The HTTP status code that your app returns to us will be displayed as the status field in the response. This is a one-time notification, and will not be retried if it fails or times out. parameters: - in: path name: id required: true schema: example: h-###-#### type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/hooks.ping_response.v1' description: Successful Response summary: Ping a webhook target tags: - hooks /v1/orders: post: description: Create an order based on an existing saved order and a new mailing list. requestBody: content: application/json: schema: $ref: '#/components/schemas/orders.resend_to_list.v1' responses: '201': content: application/json: schema: $ref: '#/components/schemas/orders.resend_created.v1' description: Successful Response security: - access_token: [] - bearer_token: [] - oauth: - orders.write summary: Create an order tags: - orders /v1/orders/saved: get: description: |- Retrieve a list of saved orders. _Note:_ For accounts with a large number of saved orders ("large" isn't specified, but is at least more than 25), only a subset of orders will be returned by this call. If you expect this to become an issue, contact us and we'll add details about accessing multiple pages of results. responses: '200': content: application/json: schema: $ref: '#/components/schemas/orders.saved_orders.v1' description: Successful Response summary: List saved orders tags: - orders /v1/orders/{id}.pdf: get: description: Retrieve the preview PDF of an order. parameters: - description: The ID of the order to be retrieved. in: path name: id required: true schema: example: m-###-#### type: string responses: '200': description: The body will be the content of the PDF. security: - access_token: [] - bearer_token: [] - oauth: - orders.read summary: Retrieve a preview PDF tags: - orders /v2/contacts: post: description: Create a new contact. requestBody: content: application/json: schema: $ref: '#/components/schemas/contacts.contact_wrapper.v1' responses: '200': content: application/json: schema: $ref: '#/components/schemas/contacts.contact_created.v1' description: Successful Response security: - access_token: [] - bearer_token: [] - oauth: - contacts.write summary: Add a contact tags: - contacts /v2/contacts/{id}: post: description: |- Update an existing contact in the database. The request body should be a JSON object containing the contact to be updated. All attributes are optional. Any omitted attributed will be left unchanged. requestBody: content: application/json: schema: $ref: '#/components/schemas/contacts.contact_wrapper.v1' responses: '204': description: Successful Response security: - access_token: [] - bearer_token: [] - oauth: - contacts.write summary: Update a contact tags: - contacts /v3/contacts: post: description: Create a new contact. requestBody: content: application/json: schema: $ref: '#/components/schemas/contacts.contact.v1' responses: '200': content: application/json: schema: $ref: '#/components/schemas/contacts.contact_created.v1' description: Successful Response security: - access_token: [] - bearer_token: [] - oauth: - contacts.write summary: Add a contact tags: - contacts /v3/contacts/{id}: post: description: |- Update an existing contact in the database. The request body should be a JSON object containing the contact object attributes to be updated. All attributes are optional. Any omitted attributed will be left unchanged. parameters: - description: The ID of the contact to be retrieved. It typically follows the format `c-###-####`, but is not guaranteed to do so, and should be treated as an opaque string. in: path name: id required: true schema: example: c-###-#### type: string requestBody: content: application/json: schema: $ref: '#/components/schemas/contacts.contact.update.v1' responses: '204': description: Successful Response security: - access_token: [] - bearer_token: [] - oauth: - contacts.write summary: Update a contact tags: - contacts servers: - url: https://www.prayerletters.com/api tags: - description: |- Webhooks let you subscribe to the events that interest you, and we'll notify you when they happen. This eliminates the need for polling, and provides your app (and therefore your app's users) with real-time updates. If you want to implement a feature in your app that currently requires polling our site for information, please contact us about setting up a webhook for the appropriate event. ## Notifications When a webhook is triggered, we will send an HTTP POST request to the target URL that you've registered. The request will always contain an `event` query parameter containing the event that triggered the webhook. Typically, each request will also contain a JSON body with more information, as well as a small set of query parameters that will provide context in case your app isn't able to read the body of the request. Your application should return a `200 OK` or `204 No Content` response to indicate that you've successfully received the notification, but any 200-series status will be accepted. ## Retries If your app isn't available or returns an error when a webhook is triggered, we will retry at increasing intervals for up to a week. Note that this can lead to notifications coming in out of order. ## Handling Loops Some integrations will have API calls that trigger webhook events. For example, an app that manages a mailing list sends address updates via the API, which will trigger a the address update webhook. In these cases, there will be a `loopback_token` and/or `loopback_client` parameter in the URL indicating that the event was triggered by the same token or client, respectively. If the webhook has a JSON body, there will also be a field with the same name and a true value at the top level of the object. You can use these two fields to filter out activity that you initiated, or to verify that it has happened. name: hooks - description: |- The Contacts API allows you to upload and download the entire contact database. You can also add, update, and remove individual contacts from the database. It's best to make changes using the Contacts API before an order is submitted. Some changes that are made to the contact database may affect existing orders, but we do not currently guarantee that a change will or will not affect an order; to change an existing order, we recommend having the user contact us directly. (We may provide stronger guarantees in the future, particularly if we're pestered to do so.) name: contacts - name: orders webhooks: contacts.address_update.v1: post: description: |- Triggered when a contact's address is updated. Note: This webhook will not be triggered when a contact is added or deleted from the mailing list, nor will it be triggered when an address is changed for a single mailing. Typical triggers include filling in missing information such as ZIP codes, providing notifications about returned mail, and sending address updates from the National Change of Address database. parameters: - description: The contact ID used by prayerletters.com. in: query name: contact_id schema: example: c-###-#### type: string - description: The contact ID supplied by your integration (or whichever integration created the contact). in: query name: external_id schema: type: string requestBody: content: application/json: schema: $ref: '#/components/schemas/contacts.webhook.address_update.v1' responses: 2XX: description: | Return a 200-series status code (200 and 204 being the most common) to indicate that your app has successfully received the webhook event. security: - access_token: [] - bearer_token: [] - oauth: - contacts.read summary: Address Updated orders.order_complete.v1: post: description: |- Triggered when production for an order is complete. Note: Orders will be shipped or mailed at the next opportunity once production is complete, which may not be the same day if the mail collection has already occurred or if a "hold until" date has been set. Note: This webhook will not be triggered when an order is canceled. parameters: - in: query name: order_id schema: example: m-###-#### type: string requestBody: content: application/json: schema: $ref: '#/components/schemas/orders.webhook.order_complete.v1' responses: 2XX: description: | Return a 200-series status code (200 and 204 being the most common) to indicate that your app has successfully received the webhook event. security: - access_token: [] - bearer_token: [] - oauth: - orders.read summary: Order Complete