Jump to content

All Activity

This stream auto-updates

  1. Earlier
  2. As many of you know, 10DLC Campaigns are required for using SMS/MMS. These Campaigns have an extensive list of requirements and can be quite involved. We've done our best to document all of this for our customers, but a lot of our guidance is spread amongst several documents, forum posts, and other resources. So, I'm using this post as a way to make it easier for you to find that information and provide some additional guidance. Enabling SMS The Campaign Registry 10DLC Registration Best Practices New Vetting Requirements Effective November 6, 2024, there will be updated requirements for several fields when submitting 10DLC campaigns. In order to ensure the most success for vetting, please make sure your campaigns follow these new requirements. What do I need to do? Ensure that your campaigns have the following new elements: Campaign and Content Attributes Subscriber Opt-in: Provide the opt-in keywords if applicable. The Opt-in Message is required and must contain the following details: Brand name, message frequency disclosure, "message and data rates may apply" disclosure, HELP information, STOP information. Subscriber Opt-Out: Provide the opt-out keywords. The Opt-out Message is required to contain the following details: Brand name and confirmation the consumer will receive no further messages. Subscriber Help: Provide the Help keywords. The Help Message must contain the following details: Brand name and an email address, phone number, or website link the consumer can use for assistance. Campaign Description Messaging frequency needs to be disclosed. If donations are collected, it must be clearly stated. Message Flow/Call to Action (CTA) (Opt-In) Provide the specific link, written form, or screenshot of the opt-in form. Use the “CTA (Call-to-Action), Privacy Policy and/or Terms and Conditions Multimedia Upload” field to attach the screenshot, if applicable. If Verbal Opt-in is collected, please add the script that describes the opt-in flow. The script must contain the following disclosures: “Brand name, types of messages being sent, message frequency disclosure, “message and data rates may apply” disclosure, HELP information, STOP/opt-out information, and a link to the Privacy Policy and Terms & Conditions” Phone Numbers cannot be a required field on the website where opt-in is collected. The only way it can be required if text messaging opt-in is the only use of the form. Please note the campaign vetting review will be on the Call to Action/Message Flow field AND the Call-to-Action disclosure provided at the actual opt-in collection. Privacy Policy and Terms and Conditions Provide the Privacy Policy URL in the Privacy Policy Link field. Provide the Terms and Conditions URL in the Terms and Conditions Link field. If you do not have your Privacy Policy and Terms and Conditions accessible via a public URL, please upload a PDF or document version of these files in the CTA (Call-to-Action), Privacy Policy and/or Terms and Conditions Multimedia Upload. Privacy Policy must include a disclaimer that no mobile opt-in will be shared with third parties for marketing purposes. Terms of Service must have an SMS disclosure that includes the brand name, types of messages consumers can expect to receive, message frequency disclosure, “message and data may apply” disclosure, privacy policy links, and opt-out instructions. Sample Messages Sample Messages must correspond to the registered use case. If a campaign is registered under multiple use cases (mixed), a sample message for each use case should be provided. For example, if you register under the marketing use case, state in the description the use of texts for promotional purposes and the sample message should reflect a typical marketing message you would send. Vetting Checklist Note: Requirements for opt-in may vary based on the use case (conversational, informational, or promotional). Brand Details If you have not included a website in your brand registration, please go back and add this in Brand Support Email Address domain should match Website/Online presence if possible Campaign and Content Attributes Subscriber Opt-In: Provide the opt-in keywords if applicable. The Opt-in Message is required and must contain the following details: Brand name Message frequency disclosure "Message and data rates may apply" disclosure HELP information and STOP information Example: "Thank you for opting in to receive recurring messages from [Company Name]. Msg frequency varies. Msg & data rates may apply. Reply HELP for help. Reply STOP to cancel." Subscriber Opt-out: Provide the opt-out keywords. The Opt-out Message is required to contain the following details: Brand name and confirmation the consumer will receive no further messages. Example: "You have successfully opted out of messages from [Company Name]. You will receive no further messages." Subscriber Help: Provide the Help keywords. The Help Message must contain the following details: Brand name Email address, phone number, or website link the consumer can use for assistance Example: "Thank you for reaching out to [Company Name]. Please call us at [phone number] or email us at [email address] for support. Reply STOP to opt-out." Number Pooling: This must be selected as "Yes" if the campaign is later submitted for a Number Pool (needing more than 49 TNs on the campaign). Direct Lending or Loan Arrangement: Must be checked "Yes" if the brand engages in lending, even if the messaging on the campaign is not related to the lending. Embedded Link: Indicates if the campaign will send embedded links in the messages. If selected "Yes", an embedded link must be included in at least one of the sample messages. Embedded Phone Number: Indicates whether the campaign will send embedded phone numbers in the messages (excluding providing a contact for HELP in the help response). If checked "Yes", an embedded phone number must be included in at least one of the sample messages. Age-Gated Content: Must be checked "Yes" if the content includes any age-gated materials. Terms & Conditions: Must be checked "Yes" and the Terms & Conditions link needs to be provided in the Terms and Conditions Link field. You can visit our Tips and Tricks article for an example Terms and Conditions. Campaign Details Campaign Description: This field is used to give a clear and detailed description of what the campaign will be used for. Ensure description aligns with registered use case (i.e., registered as 2FA campaign but campaign description references customer care messages would result in a rejection) If multiple use cases are registered, describe all use cases in the description (i.e., Low volume mixed campaign that includes 2FA and Marketing use cases. Both use cases should be mentioned in the campaign description.) If donations are collected, it must be clearly stated. Call to Action / Message Flow: This field is used to describe how a consumer opts-in to the campaign, therefore giving consent to the sender to receive their messages. The call-to-action must be explicitly clear and inform the consumer of the nature of the program. If multiple opt-in methods can be used for the same campaign, you must list them all. Clearly explains how the consumer agrees to receive text messages from the brand Provide a script, link, or attachment of the opt-in collection material (webform, physical form, verbal opt-in script, keyword marketing material, etc.). Attachments can be provided in the CTA (Call-to-Action), Privacy Policy and/or Terms and Conditions Multimedia Upload field. Note: the vetting review will include the call to action disclosure provided at the time of opt-in collection. This is not a field in TCR, but the disclosure given to the consumer when they opt-in. All opt-in methods (consumer-initiated, keyword, IVR, verbal, written forms, webforms, etc.) are required to contain the following disclosures: Brand name Types of messages being sent Message frequency disclosure “Message and data rates may apply” disclosure Help information Stop information Link to the Privacy Policy Link to the Terms and Conditions Privacy Policy Link: Use this field instead of adding links to the CTA field Privacy Policy must include a disclaimer that no mobile opt-in will be shared with third parties for marketing purposes. Terms and Conditions Link: Use this field instead of adding links to the CTA field Terms and Conditions must have an SMS disclosure that includes the types of messages consumers can expect to receive, texting cadence, message and data rate notices, privacy policy links, HELP information, and opt-out instructions. Sample Messages Sample Messages must correspond to the registered use case. If a campaign is registered under multiple use cases (mixed), a sample message for each use case should be provided. Identify the brand in the message Provide at least one sample message that includes opt out language If Embedded Link was selected “Yes” under Campaign and Content Attributes, an embedded link must be included in at least one of the sample messages. Vetting Tips and Tricks 2600Hz aggregator vets all new 10DLC campaigns to ensure campaigns are compliant with the wireless carriers’ codes of conduct. In this article, we'll provide more clarity and insight into them so you can register your campaigns successfully and as smoothly as possible. Common rejection reasons Call to Action (CTA) Opt-out message SHAFT-C content Lack of a website or online presence Non-compliance with Know Your Customer (KYC) guidelines Privacy Policy and Terms and Conditions Campaign and Content attributes Sole Proprietor campaign Privacy Policy Terms & Conditions Call to Action We often see campaigns rejected for an insufficient Call to Action/Message Flow (CTA) section. There are two portions of the Call-to-Action review when it comes to vetting for 10DLC. First, the vetting aggregator will review the Call to Action/Message Flow field in the campaign registration. This section should contain a clear and concise description of how an end user signs up to receive messages. Opt-in must be 1 to 1, can't be shared with third parties, specific for text messaging, and can't be implied. It must be clear, conspicuous, and can't be obscured within the terms & conditions and/or other agreement(s). Examples of how to get users to opt in: Entering a phone number through a website Example: Customers opt-in by visiting www.examplewebsite.com and adding their phone number. They then check a box agreeing to receive text messages from the example brand. Note: If using a website to collect opt-in, please provide a direct link to the submission form in the CTA/Message Flow field. If this is missing, the campaign will be rejected. Clicking a button on a mobile webpage Note: Please provide a website link in the CTA/Message Flow field if this is where the opt-in is being collected. Sending a message from the consumer’s mobile device that contains an advertising keyword Example: Consumers opt-in by texting START to (111) 222-3333. Important: You will need to explain how the consumer/recipient is informed to text the keyword/initiate the text messaging conversation. Acceptable explanations of how the consumer is informed include: a link to a webpage where the keyword opt-in is advertised an attached screenshot of the keyword opt-in advertisement Signing up at a point of sale (POS) or another message sender on-site location If the opt-in is collected verbally, you must provide a copy of the opt-in script read to the consumer Opting in over the phone using interactive voice response (IVR) technology Secondly, the vetting aggregator will review the actual Call-to-Action disclosure shared with the consumer/recipient during the opt-in collection. This disclosure is the language provided to the consumer/recipient informing them that they are opting in. This disclosure must contain the following information: Brand name Types of messages being sent Message frequency disclosure (Msg frequency varies, 2/msgs per week, etc.) "Message and data rates may apply" disclosure HELP information (text HELP for help) STOP/opt-out information (text STOP to stop) Link to the Privacy Policy and Terms & Conditions This information must be provided regardless of the opt-in collection method. Here are some examples of different types of opt-in: Website/Online opt-in: "By submitting this form and signing up for texts, you consent to receive marketing text messages (e.g. promos, cart reminders) from [Company Name] at the number provided, including messages sent by autodialer. Consent is not a condition of purchase. Msg & data rates may apply. Msg frequency varies. Unsubscribe at any time by replying STOP or clicking the unsubscribe link (where available). Reply HELP for help. Privacy Policy [link] & Terms [link]." Keyword Opt-in: "By texting START to [phone number], you consent to receive marketing text messages from [Company Name]. Consent is not a condition of purchase. Msg & data rates may apply. Msg frequency varies. Unsubscribe at any time by replying STOP or clicking the unsubscribe link (where available). Reply HELP for help. Privacy Policy [link] & Terms [link]." Consumer-Initiated Messaging: "By starting a text conversation with [Company Name] by texting [phone number] you are agreeing to receive conversational messages from [Company Name]. Msg & data rates may apply. Msg frequency varies. Unsubscribe at any time by replying STOP or clicking the unsubscribe link (where available). Reply HELP for help. Privacy Policy [link] & Terms [link]." Verbal opt-in: "[Company name] will be collecting opt-in verbally from their customers. The customers will be able to opt in to receive messages either in person at their physical location, or over a phone call if the customer calls. When a customer is registered for the first time, they are asked to provide the phone number, and staff is trained to ask If the customer would like to opt in to SMS-based billing notifications. They will be verbally informed that "Message and data rates may apply", "Message frequency may vary", and they can "text HELP for support or more information and STOP to unsubscribe at any time." They will also be informed that their phone number will not be shared with third parties for marketing or promotional purposes. Privacy Policy and Terms & Conditions links must be added to the Call to Action/Message Flow field in the campaign registration via TCR. Additional notes about CTAs: All traffic on behalf of a business, entity, or organization must have prior opt-in/consent. If the CTA mentions the opt-in collected on a website, the website must be provided. If it's not provided, the campaign will be declined. If donations are a part of the campaign, the Call-to-Action disclosure shared at the consent collection should reflect that. Example: "By submitting this form and signing up for texts, you consent to receive marketing, donation asks, and informational messages from [Company Name]. Msg & data rates may apply. Msg frequency varies. Unsubscribe at any time by replying STOP or clicking the unsubscribe link (where available). Reply HELP for help. Privacy Policy [link] & Terms [link]." Opt-out message Acceptable opt-out language must include at least one of the following words: END, STOP, UNSUBSCRIBE, CANCEL. If you’re using an opt-out phrase, it must be separated by spaces (i.e., STOP2END is not acceptable; it should be STOP 2 END). Please make sure that at least one of your sample messages shows your opt-out. Example: "[Insert Business Name:] You have an appointment for Tuesday at 3:00 PM, reply YES to confirm, NO to reschedule. Reply STOP to unsubscribe." SHAFT-C content The following types of content are prohibited on 10DLC: CBD, Cannabis, Sex, Hate, Alcohol*, Firearms, and Tobacco*. It’s also not allowed to be on the customer's website at all. *Alcohol and Tobacco can be supported with robust age-gating and proper opt-in. Example: If a chiropractor's office has CBD oils on its website, the campaign will be denied even if it's not directly related to CBD marketing. Lack of a website or online presence Please make sure to include any website or online presence the customer has. Even if the customer avoids putting their website, our aggregator will still search to see if there's one associated with them. If there’s prohibited content on their website, the campaign will be rejected. If they do not have a website, we recommend providing any form of online presence in the Brand Details (social media page, Google search link, etc.). They can attach the Privacy Policy and Terms & Conditions in the registration if they are not found online. Non-compliance with Know Your Customer guidelines Please follow proper Know Your Customer (KYC) guidelines for the campaign. The brand needs to reflect who will be sending the message to the customer, not the software behind the delivery. Remember that the brand is the message sender. The Employer Identification Number (EIN) and company information should reflect the message sender, not you as the reseller. Campaign and content attributes Please confirm that your campaign and content attributes are correct when setting up your campaign. Subscriber Opt-in: Provide the opt-in keywords if applicable. The Opt-in Message is required and must contain the following details: Brand name, message frequency disclosure, "message and data rates may apply" disclosure, HELP information, STOP information. Example: "Thank you for opting in to receive recurring messages from [Company Name]. Msg frequency varies. Msg & data rates may apply. Reply HELP for help. Reply STOP to cancel." Subscriber Opt-Out: Provide the opt-out keywords. The Opt-out Message is required to contain the following details: Brand name and confirmation the consumer will receive no further messages. Example: "You have successfully opted out of messages from [Company Name]. You will receive no further messages." Subscriber Help: Provide the Help keywords. The Help Message must contain the following details: Brand name and an email address, phone number, or website link the consumer can use for assistance. Example: "Thank you for reaching out to [Company Name]. Please call us at [phone number] or email us at [email address] for support. Reply STOP to opt-out." Number Pooling: This must be selected as "Yes" if the campaign is later submitted for a Number Pool (needing more than 49 TNs on the campaign). Direct Lending or Loan Arrangement: Must be checked "Yes" if the brand engages in lending, even if the messaging on the campaign is not related to the lending. Embedded Link: Indicates if the campaign will send embedded links in the messages. If selected "Yes", an embedded link must be included in at least one of the sample messages. Embedded Phone Number: Indicates whether the campaign will send embedded phone numbers in the messages (excluding providing a contact for HELP in the help response). If checked "Yes", an embedded phone number must be included in at least one of the sample messages. Age-Gated Content: Must be checked "Yes" if the content includes any age-gated materials. Terms & Conditions: Must be checked "Yes" and the Terms & Conditions link needs to be provided in the Terms and Conditions Link field or the Call to Action/Message Flow field. You can scroll down to see an example of Terms & Conditions. Sole Proprietor campaign Not all carriers accept these campaign types, so they’ll be automatically rejected. You’ll then be charged the $15 fee and need to resubmit them later, so please hold off on submitting any new Sole Proprietor campaigns until 2600Hz provides further notice. Privacy Policy All message senders must have an acceptable Privacy Policy when registering 10DLC campaigns. The most important aspect of the Privacy Policy mandates clearly describing how consumer data will be used and shared (if applicable), and how consumers can contact the message sender. A compliant Privacy Policy for 10DLC messaging should include the points below to help ensure that campaign registration and vetting are successful. Please also ensure you are linking to your privacy policy and terms and conditions in the Campaign Details section when registering your campaign. This will allow for quicker location of these items resulting in a more streamlined vetting process. Consent When a campaign is being vetted, the language presented in a sender's Privacy Policy is heavily scrutinized to ensure the message sender doesn't improperly claim to have the consumer’s consent to share end-user data with third parties for marketing purposes. While it's permissible for a business to share end-user data essential for business operations, the fundamental practice of sharing data to sell consumer information (leads) to third parties is a prohibited campaign type and will be rejected. Privacy Policies are reviewed during vetting to ensure consumer data isn't transferred among various organizations. To successfully address these requirements, we recommend adopting and including a process in the Privacy Policy that demonstrates senders will refrain from sharing information consumer data. Example: "Mobile information will not be shared with third parties/affiliates for marketing/promotional purposes. All the above categories exclude text messaging originator opt-in data and consent; this information will not be shared with any third parties." Opt-out instructions Message senders are required to acknowledge the consumer's right to opt out of a messaging campaign to ensure that message recipients’ consent remains intact. The Privacy Policy must also include instructions on how to opt out of future communications. Example: “If you wish to be removed from receiving future communications, you can opt out by texting STOP, QUIT, END, REVOKE, OPT OUT, CANCEL, or UNSUBSCRIBE.” 2600Hz strongly suggests that each brand create a personalized Privacy Policy with accompanying SMS disclosures as discussed above. 2600Hz cannot provide guidance on what is legally required within a Privacy Policy. It's the responsibility of the message sender and their provider to research and ensure the Privacy Policy meets TCPA laws, as well as, individual carrier compliance requirements. For new, non-established brands entering the messaging space, there are online resources that can help you develop the required operational processes and Privacy Policy templates that will fit the unique needs of your business. Note: If you're using online resources, your Policy, Practices, and Procedures must still include the above SMS disclosures and functions. Failure to adopt these practices may result in receiving a registration and vetting rejection (i.e., “805 - Compliant privacy policy is required on website”). Terms & Conditions All message senders must have compliant Terms & Conditions made available to their consumers/recipients. This document must be provided as a part of the campaign registration. Often, the Terms & Conditions are found on a brand's website. If the brand does not have a website, you can attach a hard copy as a PDF in the campaign registration. The Terms & Conditions page must contain the following details: Brand name Types of messages the consumer can expect to receive Message frequency disclosure "Message and data rates may apply" disclosure Customer care contact information (Text HELP for help, contact [email address] for support, etc.) Opt-out information (Text STOP to cancel) An example might look like this: "Messaging Terms & Conditions You agree to receive informational messages (appointment reminders, account notifications, etc.) from [Company Name]. Message frequency varies. Message and data rates may apply. For help, reply HELP or email us at [email address]. You can opt out at any time by replying STOP."
  3. mc_

    Hello

    Welcome @Heriberto!
  4. Hello, my name is Heriberto, and I am with Firebyte Technologies, an MSP company expanding our offerings with 2600Hz
  5. Yes, the international code 00 is used to dial out to many countries, including most of Europe, Africa, Asia, and South America
  6. Watching the thread over here.. just in case...
  7. I meant that their releasing anything open source is a "side gig" since it's not integral to their business at this point.
  8. I have been aware it has been completed, but they also mentioned waiting on the Marketplace to be completed. Sounds like a different team working on it. I would think even less time to complete that. In addition it's not a "side gig." They offer hosting for it, as well selling a deal with OneBill for a billing system.
  9. LOL -- I'm working with some companies on projects that are taking even longer than that. But the thing is, Kazoo v5 is completed and has been in production for several years. It's just the open source release of it that has been delayed. Now, that's not to say that they _couldn't_ have released it sooner if things were planned/managed better. But the thing is, they run a business, and releasing the open source code is only a "side gig" thing and isn't actually a huge benefit to the business side of things (it is some, but not nearly as much as people think). Clearly the open source release isn't their "priority" or it would have already been released, but that doesn't mean it isn't an important thing on their list; and that is why this amount of time, although painful to all the persons waiting on it, isn't unreasonable.
  10. It does not take six years to complete a project with a team. Even a single person being paid full time could have it completed.
  11. He's just speculating. Most people don't understand the complexities of a large project like Kazoo, so it's very common for many to assume that multiple delays == cancellation. When in reality that isn't the case. I don't have any inside information on it, but having worked on some large projects myself before, 2600Hz/Ooma's timeline for bringing it out, although disappointing and frustrating so far, isn't a clear indication of Kazoo v5.x staying closed source.
  12. There is an old saying which goes something like Actions speak louder than words.
  13. Did someone say that officially or are you just speculating based on the fact they keep pushing back further and further?
  14. To quote Doctor McCoy, "It's dead, Jim."
  15. @mc_ Has there been any updated target dates discussed yet on this?
  16. mc_

    Hello

    Welcome!
  17. cosmb

    Hello

    Just saying hi, I run a kazoo cluster
  18. Hello Kazoo Community, I’m currently facing a challenge with outbound SIP calls being rejected by multiple SIP trunk providers. Although I’m sending the P-Asserted-Identity header, the calls are still being rejected due to the absence of an Identity header. I understand that implementing STIR/SHAKEN or using SECSIPIDX might be necessary to resolve this issue, but I’m unsure of the best approach to achieve this. Here are a few details about my setup: 1. I need to generate and insert the Identity header dynamically for outbound calls. 2. I’m looking for guidance or examples of implementing STIR/SHAKEN or SECSIPD (Session Border Controller support, etc.) with Kazoo. Questions: • What tools or modules are recommended for generating the Identity header in Kazoo? • Are there any community-developed plugins or configurations I can use to simplify STIR/SHAKEN integration? • Can SECSIPD be integrated effectively in the Kazoo environment for this purpose, and how? • Any specific configurations or code snippets that might help to ensure compliance with STIR/SHAKEN requirements? Your insights, experiences, or links to documentation/tutorials would be greatly appreciated. Thanks in advance for your help! Best regards, Nabil
  19. Yealink has changed how you go about removing a device that is already registered to RPS. Before you could create a ticket with their support. Now you need an RPS account or a Yealink Cloud Management Service account. RPS Can be setup here: https://www.yealink.com/en/onepage/yealink-free-remote-provisioning-service Once Logged in to RPS portal. Navigate to "Device" From there in the top right is Release Occupied Device. Select Release Occupied Device and enter MAC and SN. Now the device has been removed from RPS and can now be added to kazoos for registration.
  20. until

    The link is not working for much time; Is there any update? or does it not exist anymore?
  21. No need to search for other occurrences of Iterator() — unless you have custom stuff that uses it, the Trunkstore view is the only place it appears in the Kazoo v4.3 codebase. Nice replication script—I use a simple one written in Bash but yours is more informative. One thing you can do is add some delay (minutes) between replication requests which can keep from overwhelming a server. I’ve found that if I just machine-gun style replicate a bunch of DBs sometimes RAM and CPU get slammed. Also, if worse comes to worse and you have too many issues letting Couch replicate itself, what you can do is a manual clone—in other words instead of writing a script to tell Couch what DBs to replicate, you write something that pulls each doc individually and creates it on the target machine. This way takes longer of course, but can be useful if there’s corrupted data or some other issue preventing a normal replication from succeeding fully, and you also get an internal reset of revisions on the target, since although the data is the same, it is a “new” document on the target side, and doesn’t have all the previous revision tombstones hanging around. Also, although you aren’t doing this, for the benefit of other readers, you can upgrade Bigcouch to CouchDB v3, BUT you must first upgrade to v2, and then from there to v3.
  22. We're in the process of migrating our database cluster from BigCouch to CouchDB 3.x and wanted to create a thread to document the changes required to keep a Kazoo 4.x cluster running and migration issues we've experienced. When running a replicate from BigCouch to CouchDB, we're getting a number of dropped connections that result in partially transferred databases. Re-running the replication will migrate more and more documents over until the replication is ultimately successful so we wrote a script to perform this (c# - see below). We've confirmed there are no network or firewall issues between clusters and they're even on the same subnet. Regardless, this script attached worked for us. using System.Net.Http.Json; using System.Web; using Newtonsoft.Json; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using NLog; using NLog.Config; using NLog.Targets; using NLog.Extensions.Logging; using NLog.Conditions; class Program { const string SourceHost = "x.x.x.x"; const string TargetHost = "x.x.x.x"; const string TargetUser = "username"; const string TargetPass = "password"; const int MaxReplicationAttempts = 50; static async Task<int> Main(string[] args) { // Set up a service collection var serviceCollection = new ServiceCollection(); ConfigureServices(serviceCollection); // Build the service provider var serviceProvider = serviceCollection.BuildServiceProvider(); // Get the logger var logger = serviceProvider.GetRequiredService<ILogger<Program>>(); var ListOfDatabases = new List<string>(); using (var httpClient = new HttpClient()) { try { var response = await httpClient.GetAsync($"http://{SourceHost}:5984/_all_dbs"); if (!response.IsSuccessStatusCode) { return 1; } var jsonString = await response.Content.ReadAsStringAsync(); ListOfDatabases = JsonConvert.DeserializeObject<List<string>>(jsonString) ?? []; logger.LogInformation("Received {dbCount} database strings", ListOfDatabases.Count); } catch (Exception ex) { logger.LogError(ex, "Error getting databases from source host: {sourceHost}", SourceHost); return 1; } } ListOfDatabases.Remove("_users"); ListOfDatabases.Remove("_replicator"); ListOfDatabases.Remove("_global_changes"); List<string> FailedReplications = []; int SuccessCount = 0; int FailureCount = 0; foreach (var Database in ListOfDatabases) { logger.LogInformation("----------------------------------------\n"); var handler = new HttpClientHandler { ServerCertificateCustomValidationCallback = (sender, cert, chain, sslPolicyErrors) => true }; using var httpClient = new HttpClient(handler); httpClient.DefaultRequestHeaders.UserAgent.ParseAdd("CouchDB-Replication-Tool/1.0"); // Add Basic Authentication header var authString = $"{TargetUser}:{TargetPass}"; var base64Auth = Convert.ToBase64String(System.Text.Encoding.ASCII.GetBytes(authString)); httpClient.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Basic", base64Auth); // Set the Accept header to accept all types httpClient.DefaultRequestHeaders.Accept.Add( new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("*/*")); var replicateUrl = $"http://{TargetHost}:5984/_replicate"; // Remove credentials from URL var DatabaseEncoded = HttpUtility.UrlEncode(Database); var replicationRequest = new { source = $"http://{SourceHost}:5984/{DatabaseEncoded}", target = $"http://{TargetUser}:{TargetPass}@{TargetHost}:5984/{DatabaseEncoded}", create_target = true, }; bool ReplicationComleted = false; logger.LogInformation("Starting Replication of {Database} database from {SourceHost} to {TargetHost}", Database, SourceHost, TargetHost); var jsonContent = JsonConvert.SerializeObject(replicationRequest); var content = new StringContent(jsonContent, System.Text.Encoding.UTF8, "application/json"); content.Headers.ContentLength = jsonContent.Length; int Attempts = 0; while(ReplicationComleted == false) { Attempts++; try { var response = await httpClient.PostAsync(replicateUrl, content); logger.LogInformation("Replicate request returned: {status}", response.ReasonPhrase); if (!response.IsSuccessStatusCode) { logger.LogWarning("Replicate request failed for {Database}", Database); await Task.Delay(TimeSpan.FromSeconds(15)); // Add a 1 second delay between replication attempts } else { SuccessCount++; ReplicationComleted = true; } } catch (Exception ex) { FailureCount++; FailedReplications.Add(Database); logger.LogError(ex, "Error replicating {Database}", Database); break; } if(Attempts >= MaxReplicationAttempts) { FailureCount++; FailedReplications.Add(Database); logger.LogError("Max replication attempts reached for {Database}", Database); break; } } logger.LogInformation("Done replicating {Database}", Database); } logger.LogInformation("Replication completed with {successCount} successes and {failureCount} failures", SuccessCount, FailureCount); if(FailedReplications.Count > 0) { logger.LogWarning("Failed to replicate the following databases: {failedReplications}", string.Join("\n", FailedReplications)); } return 0; } static void ConfigureServices(IServiceCollection services) { var config = new LoggingConfiguration(); // Console Target var consoleTarget = new ColoredConsoleTarget("console") { Layout = "${level:uppercase=true}|${message} ${exception:format=tostring}", EnableAnsiOutput = true, }; consoleTarget.WordHighlightingRules.Add(new ConsoleWordHighlightingRule { Text = "INFO", Condition = ConditionParser.ParseExpression("level = LogLevel.Info"), ForegroundColor = ConsoleOutputColor.Green, IgnoreCase = false, WholeWords = true }); consoleTarget.WordHighlightingRules.Add(new ConsoleWordHighlightingRule { Text = "ERROR", Condition = ConditionParser.ParseExpression("level = LogLevel.Error"), ForegroundColor = ConsoleOutputColor.Red, IgnoreCase = false, WholeWords = true }); consoleTarget.WordHighlightingRules.Add(new ConsoleWordHighlightingRule { Text = "WARN", Condition = ConditionParser.ParseExpression("level = LogLevel.Warn"), ForegroundColor = ConsoleOutputColor.Yellow, IgnoreCase = false, WholeWords = true }); /* // Set colors for different log levels consoleTarget.RowHighlightingRules.Add(new ConsoleRowHighlightingRule( condition: ConditionParser.ParseExpression("level == LogLevel.Info"), foregroundColor: ConsoleOutputColor.Green, backgroundColor: ConsoleOutputColor.White)); consoleTarget.RowHighlightingRules.Add(new ConsoleRowHighlightingRule( condition: ConditionParser.ParseExpression("level == LogLevel.Error"), foregroundColor: ConsoleOutputColor.Red, backgroundColor: ConsoleOutputColor.White)); consoleTarget.RowHighlightingRules.Add(new ConsoleRowHighlightingRule( condition: ConditionParser.ParseExpression("level == LogLevel.Warn"), foregroundColor: ConsoleOutputColor.Yellow, backgroundColor: ConsoleOutputColor.White)); */ config.AddTarget(consoleTarget); config.AddRule(NLog.LogLevel.Trace, NLog.LogLevel.Fatal, consoleTarget); LogManager.Configuration = config; services.AddLogging(loggingBuilder => { loggingBuilder.ClearProviders(); loggingBuilder.SetMinimumLevel(Microsoft.Extensions.Logging.LogLevel.Trace); loggingBuilder.AddNLog(config); }); } } In addition, CouchDB no longer listens on 5986 so an HAProxy redirect is required to keep that functioning. RuhNet helped with that and it's below: frontend couch-5986-admin-port bind 127.0.0.1:15986 default_backend couch-redir-node-admin-port backend couch-redir-node-admin-port balance roundrobin #HAProxy < 2.0 uncomment the following #reqrep ^([^\ :]*)\ /(.*) \1\ /_node/_local/\2 #HAProxy 2.0 and above uncomment the following #http-request replace-uri ^/(.*) /_node/_local/\1 server couch1 172.31.12.34:5984 check server couch2 172.31.23.45:5984 check server couch3 172.31.34.56:5984 check Lastly, CouchDB no longer supports the Interator function and needs to be replaced with .forEach. Per Ruhnet, the following needs to be done but we have yet to test this. We're writing a script that will look through all documents and check for occurrences of Iterator so they can be replaced. { "_id": "_design/trunkstore", "language": "javascript", "views": { "crossbar_listing": { "map": "function(doc) { if (doc.pvt_type != 'sys_info' || doc.pvt_deleted) return; emit(doc._id, {'realm': doc.account.auth_realm}); }", "reduce": "_count" }, "lookup_did": { "map": "function(doc) { if(doc.pvt_type != 'sys_info' || doc.pvt_deleted ) return; var realm = doc.account.auth_realm; if(doc.servers) { doc.servers.forEach(function(srv) { var auth_clone = JSON.parse(JSON.stringify(srv.auth)); auth_clone.auth_realm = realm; if (srv.options.enabled != false && srv.DIDs) { for (var did in srv.DIDs) { emit(did, { 'callerid_server': srv.callerid || '', 'callerid_account': doc.callerid || '', 'e911_callerid_server': srv.e911_callerid || '', 'e911_callerid_account': doc.e911_callerid || '', 'auth': auth_clone, 'DID_Opts': srv.DIDs[did], 'inbound_format': srv.inbound_format || 'npan', 'server': srv.options, 'account': doc.account}); } } }) } }" }, "lookup_user_flags": { "map": "function(doc) { if(doc.pvt_type != 'sys_info') return; var realm = doc.account.auth_realm; if(doc.call_restriction) { var call_restriction = JSON.parse(JSON.stringify(doc.call_restriction)) }; if(doc.servers) { var acct_clone = JSON.parse(JSON.stringify(doc.account)); doc.servers.forEach(function(srv) { if (srv.auth) { var srv_clone = JSON.parse(JSON.stringify(srv)); srv_clone.auth.auth_realm = realm; emit([realm.toLowerCase(), srv_clone.auth.auth_user.toLowerCase()], {\"server\": srv_clone, \"account\": acct_clone, \"call_restriction\": call_restriction}); } }) }}" }, "lookup_did.old": { "map": "function(doc) { if(doc.pvt_type != 'sys_info' || doc.pvt_deleted ) return; var realm = doc.account.auth_realm; if(doc.servers) { var srvs = Iterator(doc.servers); for (var srv in srvs) { var auth_clone = JSON.parse(JSON.stringify(srv[1].auth)); auth_clone.auth_realm = realm; if (srv[1].enabled != false && srv[1].DIDs) { var DIDs = Iterator(srv[1].DIDs); for (var DID in DIDs) { emit(DID[0], { 'callerid_server': srv[1].callerid || '', 'callerid_account': doc.callerid || '', 'e911_callerid_server': srv[1].e911_callerid || '', 'e911_callerid_account': doc.e911_callerid || '', 'auth': auth_clone, 'DID_Opts': DID[1], 'inbound_format': srv[1].inbound_format || 'npan', 'server': srv[1].options, 'account': doc.account}); } } } } }" }, "lookup_user_flags.old": { "map": "function(doc) { if(doc.pvt_type != 'sys_info') return; var realm = doc.account.auth_realm; if(doc.call_restriction) { var call_restriction = JSON.parse(JSON.stringify(doc.call_restriction)) }; if(doc.servers) { var acct_clone = JSON.parse(JSON.stringify(doc.account)); var srvs = Iterator(doc.servers); for (var srv in srvs) { if (srv[1].auth) { var srv_clone = JSON.parse(JSON.stringify(srv[1])); srv_clone.auth.auth_realm = realm; emit([realm.toLowerCase(), srv_clone.auth.auth_user.toLowerCase()], {\"server\": srv_clone, \"account\": acct_clone, \"call_restriction\": call_restriction}); } } }}" } } }
  23. Awesome, thank you! We've been migrating databases for the past 24 hours. For whatever reason the connection from BigCouch times out and we have to re-run the _replicate on it numerous times to finally get all the documents over. Have you seen anything like this? After running the same command a few (or a few dozen) times, everything eventually makes it over. We tried everything from connection timeout, waiting between replicates, etc with the same result. % curl -X POST http://user:pass@xx.xx.xx.xx:5984/_replicate \ -H "Content-Type: application/json" \ -d '{ "source": "http://xx.xx.xx.xx:5984/anonymous_cdrs", "target": "http://user:pass@xx.xx.xx.xx:5984/anonymous_cdrs", "create_target": true, "connection_timeout": 1000000 }' {"error":"error","reason":"{http_request_failed,\"POST\",\n \"http://xx.xx.xx.xx:5984/anonymous_cdrs/_bulk_get?latest=true&revs=true&attachments=false\",\n {error,sel_conn_closed}}"}
  1. Load more activity
×
×
  • Create New...