{"Status":true,"Message":"","Response":{"post":{"postuid":"8dd6c738-1062-498f-b3e9-0cca6873a0c6","tenantuid":"d8b744fc-2e70-4089-bb80-dd1d08f6c7b2","projectuid":"239698c5-f7eb-4574-8cc8-c6568f08b3a0","title":"One Time Passwords","slug":"article/one-time-passwords","html":"\u003Cp\u003ECustomers of DailyStory can utilize their DailyStory email and SMS marketing settings to add one time password (OTP) support for their own applications.\u003C/p\u003E\u003Ch2 id=\u0022what_is_a_one_time_password\u0022\u003EWhat is a one time password?\u003C/h2\u003E\u003Cp\u003EA One-Time Password (OTP) is a security mechanism where a user is given a password (or numeric code) that is valid for only one login session or transaction. After it\u0027s used\u2014or after a short time window\u2014it becomes invalid.\u003C/p\u003E\u003Ccite class=\u0022important\u0022\u003E\u003Cspan class=\u0022title\u0022\u003EImportant\u003C/span\u003E\u003Cp\u003EThe following content includes examples that use the DailyStory API.\u0026nbsp;\u0026nbsp;\u003Ca href=\u0022https://dev.dailystory.com/docs/create-and-use-an-api-token#/\u0022\u003EAn API key is required\u003C/a\u003E.\u0026nbsp; \u0026nbsp;\u003C/p\u003E\u003C/cite\u003E\u003Ch2 id=\u0022generating_a_one_time_password\u0022\u003EGenerating a One Time Password\u003C/h2\u003E\u003Cp\u003EMake an \u003Ccode class=\u0022inline-code\u0022\u003EHTTP POST\u003C/code\u003E request to the \u003Ca href=\u0022https://dev.dailystory.com/reference/send-one-time-password/\u0022\u003ESend One Time Password API end point\u003C/a\u003E. It must include either an email, mobile number or both. If both are present, it will default to an SMS first.\u003C/p\u003E\u003Cp\u003EFor example:\u003C/p\u003E\u003Cdiv class=\u0022code_wrapper\u0022\u003E\u003Cdiv class=\u0022code\u0022 data-language=\u0022json\u0022\u003EHTTP POST https://us-s.dailystory.com/api/v1/verification/?email=test@example.com\u0026amp;mobile=15551234567899\n\nHTTP 200 OK\n{\n    \u0026quot;Status\u0026quot;: true,\n    \u0026quot;Message\u0026quot;: \u0026quot;\u0026quot;,\n    \u0026quot;Response\u0026quot;: {\n        \u0026quot;dsid\u0026quot;: \u0026quot;3c095687f9c844d58826d5a4e994215d\u0026quot;\n    }\n}\u003C/div\u003E\u003C/div\u003E\u003Cp\u003EIn this example, the recipient will receive an SMS or email with a 6 character OTP code that is valid for 15 minutes.\u003C/p\u003E\u003Ch3 id=\u0022send_cooldown\u0022\u003ESend Cooldown\u003C/h3\u003E\u003Cp\u003EOnce an OTP is sent to a contact, the API enforces a 30-second cooldown before another OTP can be sent to the same contact. A send request made within the cooldown window returns HTTP 200 with an empty response and no OTP is sent.\u003C/p\u003E\u003Ccite class=\u0022recommended\u0022\u003E\u003Cspan class=\u0022title\u0022\u003ERecommended\u003C/span\u003E\u003Cp\u003EStore the\u0026nbsp;\u003Ccode class=\u0022inline-code\u0022\u003Edsid\u003C/code\u003E\u0026nbsp;returned from the send request and prompt the user to check their email or SMS before allowing a resend attempt.\u0026nbsp;\u0026nbsp;\u003C/p\u003E\u003C/cite\u003E\u003Ch3 id=\u0022rate_limiting\u0022\u003ERate Limiting\u003C/h3\u003E\u003Cp\u003ETo prevent abuse, DailyStory enforces a rolling rate limit on OTP generation per contact. More than \u003Cb\u003E10 send requests\u003C/b\u003E for the same contact within a \u003Cb\u003E3-hour window\u003C/b\u003E will lock further requests with an error; the window is extended on each attempt, so it clears only after 3 hours with no new requests.\u003C/p\u003E\u003Cdiv class=\u0022code_wrapper\u0022\u003E\u003Cdiv class=\u0022code\u0022 data-language=\u0022json\u0022\u003EHTTP 400\n{\n    \u0026quot;Status\u0026quot;: false,\n    \u0026quot;Message\u0026quot;: \u0026quot;The maximum number of unsuccessful OTP attempts was exceeded.\u0026quot;,\n    \u0026quot;Response\u0026quot;: {}\n}\u003C/div\u003E\u003C/div\u003E\u003Ch3 id=\u0022configurable_options_when_issuing_an_otp\u0022\u003EConfigurable Options when issuing an OTP\u003C/h3\u003E\u003Cp\u003EWhen sending an OTP several options are configurable via the API:\u003C/p\u003E\u003Ctable border=\u00221\u0022 style=\u0022border-collapse: collapse; width: 100%;\u0022\u003E\u003Cthead\u003E\u003Ctr\u003E\u003Cth\u003EOption\u003C/th\u003E\u003Cth\u003EDefault\u003C/th\u003E\u003Cth\u003EConfigurable Bounds\u003C/th\u003E\u003C/tr\u003E\u003C/thead\u003E\u003Ctr\u003E\u003Ctd\u003E\u003Cb\u003EOTP Size \u003C/b\u003E- the number of digits used to generate the OTP.\u003C/td\u003E\u003Ctd\u003E6 digits\u003C/td\u003E\u003Ctd\u003E4 - 8 digits\u003C/td\u003E\u003C/tr\u003E\u003Ctr\u003E\u003Ctd\u003E\u003Cb\u003EMinutes Valid\u003C/b\u003E - the OTP must be validated within this many minutes after being generated.\u003C/td\u003E\u003Ctd\u003E15 minutes\u003C/td\u003E\u003Ctd\u003E3 - 20 minutes\u003C/td\u003E\u003C/tr\u003E\u003Ctr\u003E\u003Ctd\u003E\u003Cb\u003ECool down\u003C/b\u003E - number of seconds before a new OTP can be generated for the recipient\u003C/td\u003E\u003Ctd\u003E30 seconds\u003C/td\u003E\u003Ctd\u003E10 seconds - 10 minutes\u003C/td\u003E\u003C/tr\u003E\u003C/table\u003E\u003Ch2 id=\u0022validating_a_one_time_password\u0022\u003EValidating a One Time Password\u003C/h2\u003E\u003Cp\u003ETo validate an OTP you will need both the OTP code and the \u003Ccode class=\u0022inline-code\u0022\u003Edsid\u003C/code\u003E associated with the OTP.\u003C/p\u003E\u003Cp\u003EMake an \u003Ccode class=\u0022inline-code\u0022\u003EHTTP GET\u003C/code\u003E request to the following API URL. In the example below the DSID is part of the URL and the otp that was received is passed in as a parameter:\u003C/p\u003E\u003Cp\u003EFor example, to verify the OTP 629316 for the dsid\u0026nbsp;3c095687f9c844d58826d5a4e994215d:\u003C/p\u003E\u003Cdiv class=\u0022code_wrapper\u0022\u003E\u003Cdiv class=\u0022code\u0022 data-language=\u0022json\u0022\u003EGET https://us-1.dailystory.com/api/v1/verification/3c095687f9c844d58826d5a4e994215d?otp=629316\n\nHTTP 200 OK\n{\n    \u0026quot;Status\u0026quot;: true,\n    \u0026quot;Message\u0026quot;: \u0026quot;The OTP is valid.\u0026quot;,\n    \u0026quot;Response\u0026quot;: {}\n}\u003C/div\u003E\u003C/div\u003E\u003Cp\u003EIf the OTP is invalid, or the validation fails:\u003C/p\u003E\u003Cdiv class=\u0022code_wrapper\u0022\u003E\u003Cdiv class=\u0022code\u0022 data-language=\u0022json\u0022\u003EHTTP 400\n{\n    \u0026quot;Status\u0026quot;: false,\n    \u0026quot;Message\u0026quot;: \u0026quot;The OTP is invalid.\u0026quot;,\n    \u0026quot;Response\u0026quot;: {}\n}\u003C/div\u003E\u003C/div\u003E\u003Ch3 id=\u0022singleuse\u0022\u003ESingle-Use\u003C/h3\u003E\u003Cp\u003EAn OTP is immediately invalidated after a successful validation and cannot be reused. Any subsequent validation attempt with the same code will return an invalid response.\u003C/p\u003E\u003Ch3 id=\u0022expiry\u0022\u003EExpiry\u003C/h3\u003E\u003Cp\u003EBy default, OTPs are valid for \u003Cb\u003E15 minutes\u003C/b\u003E from the time they are generated. An expired OTP returns the same invalid response as an incorrect code \u2014 the two cases are not distinguished in the API response.\u003C/p\u003E\u003Ch3 id=\u0022configurable_options_when_validating_an_otp\u0022\u003EConfigurable Options when validating an OTP\u003C/h3\u003E\u003Cp\u003EWhen validating an OTP several options are configurable via the API:\u003C/p\u003E\u003Ctable border=\u00221\u0022 style=\u0022border-collapse: collapse; width: 100%;\u0022\u003E\u003Cthead\u003E\u003Ctr\u003E\u003Cth\u003EOption\u003C/th\u003E\u003Cth\u003EDefault\u003C/th\u003E\u003Cth\u003EConfigurable Bounds\u003C/th\u003E\u003C/tr\u003E\u003C/thead\u003E\u003Ctr\u003E\u003Ctd\u003E\u003Cb\u003EMinutes Valid\u003C/b\u003E - the OTP must be validated within this many minutes after being generated.\u003C/td\u003E\u003Ctd\u003E15 minutes\u003C/td\u003E\u003Ctd\u003E3 - 20 minutes\u003C/td\u003E\u003C/tr\u003E\u003Ctr\u003E\u003Ctd\u003E\u003Cb\u003EAllow Retry\u003C/b\u003E - when true, the OTP survives incorrect guesses up to Retry Attempts before being invalidated. When false (default), any incorrect guess immediately invalidates the OTP.\u003C/td\u003E\u003Ctd\u003EFalse\u003C/td\u003E\u003Ctd\u003ESet to true to allow attempts to retry the OTP.\u003C/td\u003E\u003C/tr\u003E\u003Ctr\u003E\u003Ctd\u003E\u003Cb\u003ERetry Attempts\u003C/b\u003E - when allowed to retry, this determines how many attempts are allowed before lock out.\u003C/td\u003E\u003Ctd\u003E5\u003C/td\u003E\u003Ctd\u003E1 - 10\u003C/td\u003E\u003C/tr\u003E\u003C/table\u003E\u003Ch3 id=\u0022retry_lockout\u0022\u003ERetry Lockout\u003C/h3\u003E\u003Cp\u003EWhen \u003Cb\u003EAllow Retry\u003C/b\u003E is enabled and the contact exceeds the configured \u003Cb\u003ERetry Attempts\u003C/b\u003E, the OTP is invalidated and a \u003Cb\u003E3-hour lockout\u003C/b\u003E is applied to that contact. During the lockout, new OTP requests for the same contact are rejected until the window expires.\u003C/p\u003E\u003Ch2 id=\u0022error_responses\u0022\u003EError Responses\u003C/h2\u003E\u003Cp\u003EThe following table summarizes error conditions returned by the OTP API:\u003C/p\u003E\u003Ctable border=\u00221\u0022 style=\u0022border-collapse: collapse; width: 100%;\u0022\u003E\u003Cthead\u003E\u003Ctr\u003E\u003Cth\u003ECondition\u003C/th\u003E\u003Cth\u003EHTTP Status\u003C/th\u003E\u003Cth\u003EMessage\u003C/th\u003E\u003C/tr\u003E\u003C/thead\u003E\u003Ctr\u003E\u003Ctd\u003EInvalid email format\u003C/td\u003E\u003Ctd\u003E4XX\u003C/td\u003E\u003Ctd\u003ECannot send OTP to contact with an invalid email address\u003C/td\u003E\u003C/tr\u003E\u003Ctr\u003E\u003Ctd\u003EInvalid mobile number format\u003C/td\u003E\u003Ctd\u003E4XX\u003C/td\u003E\u003Ctd\u003ECannot send OTP to contact with an invalid mobile phone number\u003C/td\u003E\u003C/tr\u003E\u003Ctr\u003E\u003Ctd\u003ERate limit exceeded\u003C/td\u003E\u003Ctd\u003E4XX\u003C/td\u003E\u003Ctd\u003EThe maximum number of unsuccessful OTP attempts was exceeded.\u003C/td\u003E\u003C/tr\u003E\u003Ctr\u003E\u003Ctd\u003EOTP expired\u003C/td\u003E\u003Ctd\u003E4XX\u003C/td\u003E\u003Ctd\u003EThe OTP is invalid.\u003C/td\u003E\u003C/tr\u003E\u003Ctr\u003E\u003Ctd\u003EOTP incorrect\u003C/td\u003E\u003Ctd\u003E4XX\u003C/td\u003E\u003Ctd\u003EThe OTP is invalid.\u003C/td\u003E\u003C/tr\u003E\u003Ctr\u003E\u003Ctd\u003ENo pending OTP for dsid\u003C/td\u003E\u003Ctd\u003E4XX\u003C/td\u003E\u003Ctd\u003EThe OTP is invalid.\u003C/td\u003E\u003C/tr\u003E\u003C/table\u003E\u003Ch2 id=\u0022frequently_asked_questions\u0022\u003EFrequently Asked Questions\u003C/h2\u003E\u003Cp\u003EBelow are some frequently asked questions about one time passwords in DailyStory. The assume the default configuration, but the behavior may be different based on the parameters used.\u003C/p\u003E\u003Csection class=\u0022faq\u0022\u003E\u003Ch3\u003EWhat happens when an OTP expires?\u003C/h3\u003E\u003Carticle\u003E\u003Cp\u003EAfter 15 minutes the OTP is no longer valid. Attempting to validate an expired OTP returns the same invalid response as an incorrect code. The user must request a new OTP.\u003C/p\u003E\u003C/article\u003E\u003C/section\u003E\u003Csection class=\u0022faq\u0022\u003E\u003Ch3\u003ECan the same OTP be used more than once?\u003C/h3\u003E\u003Carticle\u003E\u003Cp\u003ENo. An OTP is deleted immediately after a successful validation. Any subsequent attempt to use the same code will return an invalid response.\u003C/p\u003E\u003C/article\u003E\u003C/section\u003E\u003Csection class=\u0022faq\u0022\u003E\u003Ch3\u003EWhat triggers the rate limit lockout?\u003C/h3\u003E\u003Carticle\u003E\u003Cp\u003EMore than 10 OTP generation requests for the same contact within a 3-hour window will trigger a lockout. The window is rolling \u2014 it is extended on each attempt, so it clears only after 3 hours with no new requests.\u003C/p\u003E\u003C/article\u003E\u003C/section\u003E\u003Csection class=\u0022faq\u0022\u003E\u003Ch3\u003EWhy didn\u0027t my customer receive a new OTP?\u003C/h3\u003E\u003Carticle\u003E\u003Cp\u003EA 30-second cooldown is enforced between sends to the same mobile number. If a send request is made within that window, the API returns a successful response but no OTP is sent. Ask the customer to wait 30 seconds before requesting a new code.\u003C/p\u003E\u003C/article\u003E\u003C/section\u003E\u003Csection class=\u0022faq\u0022\u003E\u003Ch3\u003EWhat happens if an invalid or mismatched OTP is provided during validation?\u003C/h3\u003E\u003Carticle\u003E\u003Cp\u003EIf an incorrect OTP (e.g., 123456) is submitted and does not match the code assigned to the contact, the current OTP is immediately invalidated. Even if a subsequent attempt uses the correct code, the validation will fail because the previous mismatch triggered a security reset.\u003C/p\u003E\u003C/article\u003E\u003C/section\u003E\u003Csection class=\u0022faq\u0022\u003E\u003Ch3\u003EWhat happens if my customer exceeds the retry attempts?\u003C/h3\u003E\u003Carticle\u003E\u003Cp\u003EWhen \u003Cb\u003EAllow Retry\u003C/b\u003E is enabled and the \u003Cb\u003ERetry Attempts\u003C/b\u003E limit is exceeded, the OTP is invalidated and the contact is locked out of new OTP requests for 3 hours. After the 3-hour window, the contact can request a new OTP.\u003C/p\u003E\u003C/article\u003E\u003C/section\u003E\r\n\u003Cscript type=\u0022application/ld\u002Bjson\u0022\u003E\r\n{\r\n  \u0022@context\u0022: \u0022https://schema.org\u0022,\r\n  \u0022@type\u0022: \u0022FAQPage\u0022,\r\n  \u0022mainEntity\u0022: [\r\n{\r\n    \u0022@type\u0022: \u0022Question\u0022,\r\n    \u0022name\u0022: \u0022What happens when an OTP expires?\u0022,\r\n    \u0022acceptedAnswer\u0022: {\r\n    \u0022@type\u0022: \u0022Answer\u0022,\r\n    \u0022text\u0022: \u0022After 15 minutes the OTP is no longer valid. Attempting to validate an expired OTP returns the same invalid response as an incorrect code. The user must request a new OTP.\u0022\r\n    }\r\n},\r\n{\r\n    \u0022@type\u0022: \u0022Question\u0022,\r\n    \u0022name\u0022: \u0022Can the same OTP be used more than once?\u0022,\r\n    \u0022acceptedAnswer\u0022: {\r\n    \u0022@type\u0022: \u0022Answer\u0022,\r\n    \u0022text\u0022: \u0022No. An OTP is deleted immediately after a successful validation. Any subsequent attempt to use the same code will return an invalid response.\u0022\r\n    }\r\n},\r\n{\r\n    \u0022@type\u0022: \u0022Question\u0022,\r\n    \u0022name\u0022: \u0022What triggers the rate limit lockout?\u0022,\r\n    \u0022acceptedAnswer\u0022: {\r\n    \u0022@type\u0022: \u0022Answer\u0022,\r\n    \u0022text\u0022: \u0022More than 10 OTP generation requests for the same contact within a 3-hour window will trigger a lockout. The window is rolling \u2014 it is extended on each attempt, so it clears only after 3 hours with no new requests.\u0022\r\n    }\r\n},\r\n{\r\n    \u0022@type\u0022: \u0022Question\u0022,\r\n    \u0022name\u0022: \u0022Why didn\u0027t my customer receive a new OTP?\u0022,\r\n    \u0022acceptedAnswer\u0022: {\r\n    \u0022@type\u0022: \u0022Answer\u0022,\r\n    \u0022text\u0022: \u0022A 30-second cooldown is enforced between sends to the same mobile number. If a send request is made within that window, the API returns a successful response but no OTP is sent. Ask the customer to wait 30 seconds before requesting a new code.\u0022\r\n    }\r\n},\r\n{\r\n    \u0022@type\u0022: \u0022Question\u0022,\r\n    \u0022name\u0022: \u0022What happens if an invalid or mismatched OTP is provided during validation?\u0022,\r\n    \u0022acceptedAnswer\u0022: {\r\n    \u0022@type\u0022: \u0022Answer\u0022,\r\n    \u0022text\u0022: \u0022If an incorrect OTP (e.g., 123456) is submitted and does not match the code assigned to the contact, the current OTP is immediately invalidated. Even if a subsequent attempt uses the correct code, the validation will fail because the previous mismatch triggered a security reset.\u0022\r\n    }\r\n},\r\n{\r\n    \u0022@type\u0022: \u0022Question\u0022,\r\n    \u0022name\u0022: \u0022What happens if my customer exceeds the retry attempts?\u0022,\r\n    \u0022acceptedAnswer\u0022: {\r\n    \u0022@type\u0022: \u0022Answer\u0022,\r\n    \u0022text\u0022: \u0022When Allow Retry is enabled and the Retry Attempts limit is exceeded, the OTP is invalidated and the contact is locked out of new OTP requests for 3 hours. After the 3-hour window, the contact can request a new OTP.\u0022\r\n    }\r\n}\r\n    ]\r\n}\r\n\u003C/script\u003E","publish_status":0,"post_type":"Article","authoruid":"3dde8c16-763a-4a2b-ae0b-1d8c50c62e3d","author":{"authoruid":"3dde8c16-763a-4a2b-ae0b-1d8c50c62e3d"},"featured_image_updating":false,"meta_description":"Enable OTP support in your app using DailyStory email/SMS settings and API. Learn how to generate and validate One-Time Passwords for secure access.","keywords":"one-time password;OTP;API;HTTP POST;validation;rate limiting;expiry;single-use;send cooldown;error responses","display_toc":true,"has_workingcopy":false,"allow_indexing":true,"total_views":565,"date_published":"2025-09-17T16:52:00","date_updated":"2026-04-17T13:29:26.873","date_created":"2025-09-17T16:32:07.78"}}}